Create and run .NET code from Standard workflows in Azure Logic Apps
Applies to: Azure Logic Apps (Standard)
For integration solutions where you have to author and run .NET code from your Standard logic app workflow, you can use Visual Studio Code with the Azure Logic Apps (Standard) extension. This extension provides the following capabilities and benefits:
- Write your own code by creating functions that have the flexibility and control to solve your most challenging integration problems.
- Debug code locally in Visual Studio Code. Step through your code and workflows in the same debugging session.
- Deploy code alongside your workflows. No other service plans are necessary.
- Support BizTalk Server migration scenarios so you can lift-and shift custom .NET investments from on premises to the cloud.
With the capability to write your own code, you can accomplish scenarios such as the following:
- Custom business logic implementation
- Custom parsing to extract information from an inbound message
- Data validation and simple transformations
- Message shaping for outbound messages to another system, such as an API
- Calculations
This capability isn't suitable for scenarios such as the following:
- Processes that take more than 10 minutes to run
- Large message and data transformations
- Complex batching and debatching scenarios
- BizTalk Server pipeline components that implement streaming
For more information about limitations in Azure Logic Apps, see Limits and configuration - Azure Logic Apps.
Prerequisites
An Azure account and subscription. If you don't have a subscription, sign up for a free Azure account.
The most recent Visual Studio Code with the Azure Logic Apps (Standard) extension. To meet these requirements, see the prerequisites for Create Standard workflows in single-tenant Azure Logic Apps with Visual Studio Code.
The custom functions capability is currently available only in Visual Studio Code, running on a Windows operating system.
The custom functions capability currently supports calling .NET Framework and .NET 8 for Azure-hosted logic app workflows.
A local folder to use for creating your code project
Limitations
Custom functions authoring currently isn't available in the Azure portal. However, after you deploy your functions from Visual Studio Code to Azure, follow the steps in Call your code from a workflow for the Azure portal. You can use the built-in action named Call a local function in this logic app to select from your deployed custom functions and run your code. Subsequent actions in your workflow can reference the outputs from these functions, as in any other workflow. You can view the built-in action's run history, inputs, and outputs.
Custom functions use an isolated worker to invoke the code in your logic app workflow. To avoid package references conflicts between your own function code and the worker, use the same package versions referenced by the worker. For the full package list and versions referenced by the worker, see Worker and package dependencies.
Create a code project
The latest Azure Logic Apps (Standard) extension for Visual Studio Code includes a code project template that provides a streamlined experience for writing, debugging, and deploying your own code with your workflows. This project template creates a workspace file and two sample projects: one project to write your code, the other project to create your workflows.
Note
You can't use the same project folder for both your code and workflows.
Open Visual Studio Code. On the Activity Bar, select the Azure icon. (Keyboard: Shift+Alt+A)
In the Azure window that opens, on the Workspace section toolbar, from the Azure Logic Apps menu, select Create new logic app workspace.
In the Select folder box, browse to and select the local folder that you created for your project.
When the Create new logic app workspace prompt box appears, provide a name for your workspace:
This example continues with MyLogicAppWorkspace.
When the Select a project template for your logic app workspace prompt box appears, select Logic app with custom code project.
For Azure-hosted Standard logic app workflows, follow the prompt to select either .NET Framework or .NET 8.
Follow the subsequent prompts to provide the following example values:
Item Example value Function name for your .NET functions project WeatherForecast Namespace name for your .NET functions project Contoso.Enterprise Workflow template:
- Stateful Workflow
- Stateless WorkflowStateful Workflow Workflow name MyWorkflow Select Open in current window.
After you finish this step, Visual Studio Code creates your workspace, which includes a .NET functions project and a logic app project, by default, for example:
Node Description <workspace-name> Contains both your .NET functions project and logic app workflow project. Functions Contains the artifacts for your .NET functions project. For example, the <function-name>.cs file is the code file where you can author your code. LogicApp Contains the artifacts for your logic app project, including a blank workflow.
Write your code
In your workspace, expand the Functions node, if not already expanded.
Open the <function-name>.cs file, which is named WeatherForecast.cs in this example.
By default, this file contains sample code that has the following code elements along with the previously provided example values where appropriate:
- Namespace name
- Class name
- Function name
- Function parameters
- Return type
- Complex type
The following example shows the complete sample code:
//------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ namespace Contoso.Enterprise { using System; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.Azure.Functions.Extensions.Workflows; using Microsoft.Azure.WebJobs; using Microsoft.Extensions.Logging; /// <summary> /// Represents the WeatherForecast flow invoked function. /// </summary> public class WeatherForecast { private readonly ILogger<WeatherForecast> logger; public WeatherForecast(ILoggerFactory loggerFactory) { logger = loggerFactory.CreateLogger<WeatherForecast>(); } /// <summary> /// Executes the logic app workflow. /// </summary> /// <param name="zipCode">The zip code.</param> /// <param name="temperatureScale">The temperature scale (e.g., Celsius or Fahrenheit).</param> [FunctionName("WeatherForecast")] public Task<Weather> Run([WorkflowActionTrigger] int zipCode, string temperatureScale) { this.logger.LogInformation("Starting WeatherForecast with Zip Code: " + zipCode + " and Scale: " + temperatureScale); // Generate random temperature within a range based on the temperature scale Random rnd = new Random(); var currentTemp = temperatureScale == "Celsius" ? rnd.Next(1, 30) : rnd.Next(40, 90); var lowTemp = currentTemp - 10; var highTemp = currentTemp + 10; // Create a Weather object with the temperature information var weather = new Weather() { ZipCode = zipCode, CurrentWeather = $"The current weather is {currentTemp} {temperatureScale}", DayLow = $"The low for the day is {lowTemp} {temperatureScale}", DayHigh = $"The high for the day is {highTemp} {temperatureScale}" }; return Task.FromResult(weather); } /// <summary> /// Represents the weather information for WeatherForecast. /// </summary> public class Weather { /// <summary> /// Gets or sets the zip code. /// </summary> public int ZipCode { get; set; } /// <summary> /// Gets or sets the current weather. /// </summary> public string CurrentWeather { get; set; } /// <summary> /// Gets or sets the low temperature for the day. /// </summary> public string DayLow { get; set; } /// <summary> /// Gets or sets the high temperature for the day. /// </summary> public string DayHigh { get; set; } } } }
The function definition includes a default
Run
method that you can use to get started. This sampleRun
method demonstrates some of the capabilities available with the custom functions feature, such as passing different inputs and outputs, including complex .NET types.The <function-name>.cs file also includes the
ILogger
interface, which provides support for logging events to an Application Insights resource. You can send tracing information to Application Insights and store that information alongside the trace information from your workflows, for example:private readonly ILogger<WeatherForecast> logger; public WeatherForecast(ILoggerFactory loggerFactory) { logger = loggerFactory.CreateLogger<WeatherForecast>(); } [FunctionName("WeatherForecast")] public Task<Weather> Run([WorkflowActionTrigger] int zipCode, string temperatureScale) { this.logger.LogInformation("Starting WeatherForecast with Zip Code: " + zipCode + " and Scale: " + temperatureScale); <...> }
Replace the sample function code with your own, and edit the default
Run
method for your own scenarios. Or, you can copy the function, including the[FunctionName("<*function-name*>")]
declaration, and then rename the function with a unique name. You can then edit the renamed function to meet your needs.
This example continues with the sample code without any changes.
Compile and build your code
After you finish writing your code, compile to make sure that no build errors exist. Your .NET functions project automatically includes build tasks, which compile and then add your code to the lib\custom folder in your logic app project where workflows look for custom functions to run. These tasks put the assemblies in the lib\custom\net472 or lib\custom\net8 folder, based on your .NET version.
In Visual Studio Code, from the Terminal menu, select New Terminal.
From the working directory list that appears, select Functions as your current working directory for the new terminal.
Visual Studio Code opens a terminal window with a command prompt.
In the Terminal window, at the command prompt, enter dotnet restore.
Visual Studio Code analyzes your projects and determines whether they're up-to-date.
After the command prompt reappears, enter dotnet build. Or, from the Terminal menu, select Run Task. From the task list, select build (Functions).
If your build succeeds, the Terminal window reports that the Build succeeded.
Confirm that the following items exist in your logic app project:
In your workspace, expand the following folders: LogicApp > lib\custom > net472 or net8, based on your .NET version. Confirm that the subfolder named net472 or net8, respectively, contains the assembly (DLL) files required to run your code, including a file named <function-name>.dll.
In your workspace, expand the following folders: LogicApp > lib\custom > <function-name>. Confirm that the subfolder named <function-name> contains a function.json file, which includes the metadata about the function code that you wrote. The workflow designer uses this file to determine the necessary inputs and outputs when calling your code.
The following example shows sample generated assemblies and other files in the logic app project:
Call your code from a workflow
After you confirm that your code compiles and that your logic app project contains the necessary files for your code to run, open the default workflow that's included with your logic app project.
In your workspace, under LogicApp, expand the <workflow-name> node, open the shortcut menu for workflow.json, and select Open Designer.
On the workflow designer that opens, the default workflow, included with your logic app project, appears with the following trigger and actions:
- The built-in Request trigger named When a HTTP request is received
- The built-in action named Call a local function in this logic app
- The built-in Response action named Response, which you use to reply to the caller only when you use the Request trigger
Select the action named Call a local function in this logic app.
The action's information pane opens to the right.
Review and confirm that the Function Name parameter value is set to the function that you want to run. Review or change any other parameter values that your function uses.
Debug your code and workflow
Repeat the following steps to start the Azurite storage emulator three times: one time each for the following Azure Storage services:
- Azure Blob Service
- Azure Queue Service
- Azure Table Service
From the Visual Studio Code View menu, select Command Palette.
At the prompt that appears, find and select Azurite: Start Blob Service.
From the working directory list that appears, select LogicApp.
Repeat these steps for Azurite: Start Queue Service and Azurite: Start Table Service.
You're successful when the Visual Studio Code taskbar at the bottom of the screen shows the three storage services running, for example:
Attach the debugger to your logic app project by following these steps:
On the Visual Studio Code Activity Bar, select Run and Debug. (Keyboard: Ctrl+Shift+D)
From the Run and Debug list, select Attach to logic app (LogicApp), if not already selected, and then select Play (green arrow).
The Terminal window opens and shows the started debugging process. The Debug Console window then appears and shows the debugging statuses. At the bottom of Visual Studio Code, the task bar turns orange, indicating that the .NET debugger is loaded.
Attach the debugger to your .NET functions project by following these steps, based on your code:
.NET 8 projects
From the Visual Studio Code View menu, select Command Palette.
From the command palette, find and select Debug: Attach to a .NET 5+ or .NET Core process.
From the list, find and select the dotnet.exe process. If multiple dotnet.exe processes exist, select the process that has the following path:
<drive-name>:\Users<user-name>.azure-functions-core-tools\Functions\ExtensionBundles\Microsoft.Azure.Functions.ExtensionBundle.Workflows<extension-bundle-version>\CustomCodeNetFxWorker\net8\Microsoft.Azure.Workflows.Functions.CustomCodeNetFxWorker.dll
.NET Framework projects
From the Run and Debug list, select Attach to .NET Functions (Functions), if not already selected, and then select Play (green arrow).
To set any breakpoints, in your function definition (<function-name>.cs) or workflow definition (workflow.json), find the line number where you want the breakpoint, and select the column to the left, for example:
To manually run the Request trigger in your workflow, open the workflow's Overview page.
From your logic app project, open the workflow.json file's shortcut menu, and select Overview.
On the workflow's Overview page, the Run trigger button is available for when you want to manually start the workflow. Under Workflow Properties, the Callback URL value is the URL for a callable endpoint that's created by the Request trigger in your workflow. You can send requests to this URL to trigger your workflow from other apps, including other logic app workflows.
On the Overview page toolbar, select Run trigger.
After your workflow starts to run, the debugger activates your first breakpoint.
On the Run menu or debugger toolbar, select a debug action.
After the workflow run completes, the Overview page shows the finished run and basic details about that run.
To review more information about the workflow run, select the finished run. Or, from the list next to the Duration column, select Show run.
Deploy your code
You can deploy your custom functions in the same way that you deploy your logic app project. Whether you deploy from Visual Studio Code or use a CI/CD DevOps process, make sure that you build your code and that all dependent assemblies exist in the following logic app project folder before you deploy:
.NET 4.7.2: lib/custom/net472 folder
.NET 8: lib/custom/net8 folder
For more information, see Deploy Standard workflows from Visual Studio Code to Azure.
Troubleshoot problems
Action information pane error
On the workflow designer, when you select the built-in action named Call a local function in this logic app, the action's information pane shows the following message:
Failed to retrieve dynamic inputs. Error details:
In this scenario, examine your logic app project to check whether the LogicApp\lib\custom folder is empty. If empty, from the Terminal menu, select Run Task > build Functions.
No process with the specified name is currently running
If you get this error message when you run your workflow, you likely have the debugger process attached to .NET Functions, rather than to your logic app.
To fix this problem, from the Run and Debug list, select Attach to logic app (LogicApp), and then select Play (green triangle).
Package not imported correctly
If the Output window shows an error similar to the following message, make sure that you have at least .NET 6.0 installed. If you have this version installed, try uninstalling and then reinstalling.
C:\Users\yourUserName\.nuget\packages\microsoft.net.sdk.functions\4.2.0\build\Microsoft.NET.Sdk.Functions.targets(83,5): warning : The ExtensionsMetadataGenerator package was not imported correctly. Are you missing 'C:\Users\yourUserName\.nuget\packages\microsoft.azure.webjobs.script.extensionsmetadatagenerator\4.0.1\build\Microsoft.Azure.WebJobs.Script.ExtensionsMetadataGenerator.targets' or 'C:\Users\yourUserName\.nuget\packages\microsoft.azure.webjobs.script.extensionsmetadatagenerator\4.0.1\build\Microsoft.Azure.WebJobs.Script.ExtensionsMetadataGenerator.props'? [C:\Desktop\...\custom-code-project\MyLogicAppWorkspace\Function\WeatherForecast.csproj] WeatherForecast -> C:\Desktop\...\custom-code-project\MyLogicAppWorkspace\Function\\bin\Debug\net472\WeatherForecast.dll C:\Users\yourUserName\.nuget\packages\microsoft.net.sdk.functions\4.2.0\build\Microsoft.NET.Sdk.Functions.Build.targets(32,5): error : It was not possible to find any compatible framework version [C:\Desktop\...\custom-code-project\MyLogicAppWorkspace\Function\WeatherForecast.csproj] C:\Users\yourUserName\.nuget\packages\microsoft.net.sdk.functions\4.2.0\build\Microsoft.NET.Sdk.Functions.Build.targets(32,5): error : The specified framework 'Microsoft.NETCore.App', version '6.0.0' was not found. [C:\Desktop\...\custom-code-project\MyLogicAppWorkspace\Function\WeatherForecast.csproj] C:\Users\yourUserName\.nuget\packages\microsoft.net.sdk.functions\4.2.0\build\Microsoft.NET.Sdk.Functions.Build.targets(32,5): error : - Check application dependencies and target a framework version installed at: [C:\Desktop\...\custom-code-project\MyLogicAppWorkspace\Function\WeatherForecast.csproj]
Build failures
If your function doesn't include variables, and you build your code, the Output window might show the following error messages:
C:\Users\yourUserName\...\custom-code-project\Function\func.cs (24,64): error CS1031: Type expected [C:\Users\yourUserName\...\custom-code-project\Function\func.csproj]
C:\Users\yourUserName\...\custom-code-project\Function\func.cs (24,64): error CS1001: Identifier expected [C:\Users\yourUserName\...\custom-code-project\Function\func.csproj]
Build FAILED.
C:\Users\yourUserName\...\custom-code-project\Function\func.cs (24,64): error CS1031: Type expected [C:\Users\yourUserName\...\custom-code-project\Function\func.csproj]
C:\Users\yourUserName\...\custom-code-project\Function\func.cs (24,64): error CS1001: Identifier expected [C:\Users\yourUserName\...\custom-code-project\Function\func.csproj]
0 Warning(s)
2 Error(s)
To fix this problem, in your code's Run
method, append the following parameter:
string parameter1 = null
The following example shows how the Run
method signature appears:
public static Task<Weather> Run([WorkflowActionTrigger] int zipCode, string temperatureScale, string parameter1 = null)