Add and run C# scripts inline with Standard workflows for Azure Logic Apps (Preview)
Applies to: Azure Logic Apps (Standard)
Note
This capability is in preview and is subject to the Supplemental Terms of Use for Azure Previews.
To perform custom integration tasks inline with your Standard workflow in Azure Logic Apps, you can directly add and run C# scripts from within your workflow. For this task, use the Inline Code action named Execute CSharp Script Code. This action returns the results from your script so that you can use this output in your workflow's subsequent actions.
This capability provides the following benefits:
Write your own scripts within the workflow designer so that you can solve more complex integration challenges without having to use Azure Functions. No other service plans are necessary.
This benefit streamlines workflow development plus reduces the complexity and cost with managing more services.
Generate a dedicated code file, which provides a personalized scripting space within your workflow.
Deploy scripts alongside your workflows.
This guide shows how to add the action in your workflow and add the C# script code that you want to run.
Prerequisites
An Azure account and subscription. If you don't have a subscription, sign up for a Azure account.
The Standard logic app workflow where you want to add your C# script. The workflow must already start with a trigger. For more information, see Create example Standard logic app workflows.
You can use any trigger for your scenario, but as an example, this guide uses the Request trigger named When a HTTP request is received and also the Response action. The workflow runs when another application or workflow sends a request to the trigger's endpoint URL. The sample script returns the results from code execution as output that you can use in subsequent actions.
Example scenarios
The following list describes some example scenarios where you can use a script helps with certain integration tasks:
Parse and perform transformations or manipulations on a payload beyond the built-in expressions and data operations capabilities. For example, you can use a script to return a modified schema for downstream processing.
Manage Azure resources such as virtual machines and start or step them, based on some business logic.
Run a stored procedure on a SQL server that needs to run on a schedule and store the results on SharePoint.
Log workflow errors with detailed information by saving to Azure Storage or to email or notify your team.
Encrypt and decrypt data to comply with API security standards.
Pass a file into the script to zip or unzip for an HTTP request.
Aggregate data from various APIs and files to create daily reports
Considerations
The Azure portal saves your script as a C# script file (.csx) in the same folder as your workflow.json file, which stores the JSON definition for your workflow, and deploys the file to your logic app resource along with the workflow definition. Azure Logic Apps compiles this file to make the script ready for execution.
The .csx file format lets you write less "boilerplate" and focus just on writing a C# function. You can rename the .csx file for easier management during deployment. However, each time you rename the script, the new version overwrites the previous version.
The script is local to the workflow. To use the same script in other workflows, view the script file in the KuduPlus console, and then copy the script to reuse in other workflows.
Limitations
Name | Limit | Notes |
---|---|---|
Script run duration | 10 minutes | If you have scenarios that need longer durations, use the product feedback option to provide more information about your needs. |
Output size | 100 MB | Output size depends on the output size limit for actions, which is generally 100 MB. |
Add the Execute CSharp Script Code action
In the Azure portal, open your Standard logic app resource and workflow in the designer.
In the designer, follow these general steps to add the Inline Code Operations action named Execute CSharp Script Code to your workflow.
After the action information pane opens, on the Parameters tab, in the Code File box, update the prepopluated sample code with your own script code.
At the top of the script, import the necessary namespaces and add any required assembly references as usual.
Implement the
Run
method:The
Run
method name is predefined, and your workflow executes only by calling this Run method at runtime.To access data coming from your workflow, the
Run
method accepts this data through a parameter with WorkflowContext type. You can use the WorkflowContext object for the following tasks:To return the script's results or other data to your workflow, implement the
Run
method with a return type. For more information, see Return data to your workflow.To log the output from your script in C#, implement the
Run
method to accept a function logger through a parameter withILogger
type, and uselog
as the argument name for easy identification. Avoid includingConsole.Write
in your script.Important
If you have a long-running script that requires graceful termination in case the function host shuts down, include a cancellation token, which is required, with your function logger.
For more information, see the following sections:
The following example shows the action's Parameters tab with the sample script code:
The following example shows the sample script code:
/// 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; /// <summary> /// Executes the inline C# code. /// </summary> /// <param name="context">The workflow context.</param> /// <remarks> The entry-point to your code. The function signature should remain unchanged.</remarks> public static async Task<Results> Run(WorkflowContext context, ILogger log) { var triggerOutputs = (await context.GetTriggerResults().ConfigureAwait(false)).Outputs; /// Dereferences the 'name' property from the trigger payload. var name = triggerOutputs?["body"]?["name"]?.ToString(); /// To get the outputs from a preceding action, you can uncomment and repurpose the following code. // var actionOutputs = (await context.GetActionResults("<action-name>").ConfigureAwait(false)).Outputs; /// The following logs appear in the Application Insights traces table. // log.LogInformation("Outputting results."); // var name = null; return new Results { Message = !string.IsNullOrEmpty(name) ? $"Hello {name} from CSharp action" : "Hello from CSharp action." }; } public class Results { public string Message {get; set;} }
For more information, see "#r" - Reference external assemblies.
When you finish, save your workflow.
After you run your workflow, you can review the workflow output in Application Insights, if enabled. For more information, see View logs in Application Insights.
Import namespaces
To import namespaces, do so with the using
clause as usual. The following list includes automatically imported namespaces, so they're optional for you to include in your script:
System
System.Collections.Generic
System.IO
System.Linq
System.Net.Http
System.Threading.Tasks
Microsoft.Azure.WebJobs
Microsoft.Azure.WebJobs.Host
Add references to external assemblies
To reference .NET Framework assemblies, use the #r "<assembly-name>
directive, for example:
/// 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)
{
<...>
}
public class Results
{
<...>
}
The following list includes assemblies 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
Log output to a stream
In your Run
method, include a parameter with ILogger
type and log
as the name, for example:
public static void Run(WorkflowContext context, ILogger log)
{
log.LogInformation($"C# script successfully executed.");
}
Log output to Application Insights
To create custom metrics in Application Insights, use the LogMetric
extension method on ILogger
.
The following example shows a sample method call:
logger.LogMetric("TestMetric", 1234);
Access workflow trigger and action outputs in your script
To access data from your workflow, use the following methods available for the WorkflowContext
context object:
GetTriggerResults
methodTo access trigger outputs, use this method to return an object that represents the trigger and its outputs, which are available through the
Outputs
property. This object has JObject type, and you can use the square brackets ([]) as an indexer to access various properties in the trigger outputs.The following example gets the data from the
body
property in the trigger outputs:public static async Task<Results> Run(WorkflowContext context, ILogger log) { var triggerOutputs = (await context.GetTriggerResults().ConfigureAwait(false)).Outputs; var body = triggerOutputs["body"]; return new Results; } public class Results { <...> }
GetActionResults
methodTo access action outputs, use this method to return an object that represents the action and its outputs, which are available through the
Outputs
property. This method accepts an action name as a parameter. The following example gets the data from thebody
property in the outputs from an action named action-name:public static async Task<Results> Run(WorkflowContext context, ILogger log) { var actionOutputs = (await context.GetActionResults("action-name").ConfigureAwait(false)).Outputs; var body = actionOutputs["body"]; return new Results; } public class Results { <...> }
Access environment variables or app setting value
To get an environment variable or an app setting value, use the System.Environment.GetEnvironmentVariable
method, for 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);
}
Return data to your workflow
For this task, implement your Run
method with a return type and return
statement. If you want an asynchronous version, implement the Run
method with a Task<return-type>
attribute and the async
keyword. The return value is set to the script action's outputs body
property, which any subsequent workflow actions can then reference.
The following example shows a Run
method with a Task<Results>
attribute, the async
keyword, and a return
statement:
public static async Task<Results> Run(WorkflowContext context, ILogger log)
{
return new Results
{
Message = !string.IsNullOrEmpty(name) ? $"Returning results with status message."
};
}
public class Results
{
public string Message {get; set;}
}
View the script file
In the Azure portal, open your Standard logic app resource that has the workflow you want.
On the logic app resource menu, under Development Tools, select Advanced Tools.
On the Advanced Tools page, select Go, which opens the KuduPlus console.
Open the Debug console menu, and select CMD.
Go to your logic app's root location: site/wwwroot
Go to your workflow's folder, which contains the .csx file, along this path: site/wwwroot/{workflow-name}
Next to the file name, select Edit to open and view the file.
View logs in Application Insights
In the Azure portal, on the logic app resource menu, under Settings, select Application Insights, and then select your logic app.
On the Application Insights menu, under Monitoring, select Logs.
Create a query to find any traces or errors from your workflow execution, for example:
union traces, errors | project TIMESTAMP, message
Compilation errors
In this release, the web-based editor includes limited IntelliSense support, which is still under improvement. Any compilation errors are detected when you save your workflow, and the Azure Logic Apps runtime compiles your script. These errors appear in your logic app's error logs.
Runtime errors
If an error happens when your script executes, Azure Logic Apps performs these steps:
- Passes the error back to your workflow.
- Marks the script action as Failed.
- Provides an error object that represents the exception thrown from your script.
The following example shows a sample error:
The function 'CSharp_MyLogicApp-InvalidAction_execute_csharp_script_code.csx' failed with the error 'The action 'nonexistent' does not exist in the workflow.' when executing. Please verify function code is valid.
Example scripts
The following example scripts perform various tasks that you might
Decompress a ZIP file with text files from an HTTP action into a string array
// 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 C# 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>();
// Creates an in-memory stream from the zip bytes.
using (MemoryStream zipStream = new MemoryStream(zipBytes))
{
// Extracts 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());
}
}
}
WorkflowContext class
Represents a workflow context.
Methods
GetActionResult(string actionName)
Gets the result from a specific action in the workflow.
The asynchronous version uses Task<> as the return type, for example:
Task<WorkflowOperationResult> GetActionResult(string actionName)
Parameters
actionName
: The action name.
Returns
The asynchronous version returns a Task
object that represents the asynchronous operation. The task result contains a WorkflowOperationResult
object. For information about the WorkflowOperationResult object properties, see WorkflowOperationResult class.
RunTriggerResult()
Gets the result from the trigger in the workflow.
The asynchronous version uses Task<> as the return type, for example:
Task<WorkflowOperationResult> RunTriggerResult()
Parameters
None.
Returns
The asynchronous version returns a Task
object that represents the asynchronous operation. The task result contains a WorkflowOperationResult
object. For information about the WorkflowOperationResult object properties, see WorkflowOperationResult class.
WorkflowOperationResult class
Represents the result from a workflow operation.
Properties
Name | Type | Description |
---|---|---|
Name | String | Gets or sets the operation name. |
Inputs | JToken | Gets or sets the operation execution inputs. |
Outputs | JToken | Gets or sets the operation execution outputs. |
StartTime | DateTime? | Gets or sets the operation start time. |
EndTime | DateTime? | Gets or sets the operation end time. |
OperationTrackingId | String | Gets or sets the operation tracking ID. |
Code | String | Gets or sets the status code for the action. |
Status | String | Gets or sets the status for the action. |
Error | JToken | Gets or sets the error for the action. |
TrackedProperties | JToken | Gets or sets the tracked properties for the action. |