Azure Logic Apps — Calling Azure Functions
Integrating Serverless Code into Workflows
Introduction
Azure Functions provide a powerful way to run custom code in response to events or on a schedule. When combined with Logic Apps, you can create sophisticated workflows that leverage the strengths of both platforms — Logic Apps for orchestration and integration, and Azure Functions for complex business logic.
This comprehensive guide covers:
- Calling functions — Invoking functions from Logic Apps
- Authentication — Using managed identity for secure access
- Data passing — Sending and receiving data between systems
- Error handling — Managing function failures
- Real-world patterns — Common integration scenarios
How It Works
Architecture
┌─────────────────────────────────────────────────────────────────────┐
│ LOGIC APPS + AZURE FUNCTIONS │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ LOGIC APP WORKFLOW │ │
│ │ │ │
│ │ ┌──────────┐ │ │
│ │ │ Trigger │──────┐ │ │
│ │ └──────────┘ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────┐ │ │
│ │ │ Invoke Azure Function │ │ │
│ │ │ - URL: /api/processorder │ ──────────────────────┐ │ │
│ │ │ - Method: POST │ │ │ │
│ │ │ - Body: {order data} │ │ │ │
│ │ └──────────────────────────────┘ │ │ │
│ │ │ │ │ │
│ │ ▼ │ │ │
│ │ ┌──────────────────────────────┐ │ │ │
│ │ │ Process Response │ │ │ │
│ │ │ - Use function output │ │ │ │
│ │ │ - Continue workflow │ │ │ │
│ │ └──────────────────────────────┘ │ │ │
│ └──────────────────────────────────────────────────────────│──┘ │
│ │ │ │
│ │ HTTPS │ │
│ ▼ │ │
│ ┌──────────────────────────────────────────────────────────│──┐ │
│ │ AZURE FUNCTION │ │
│ │ │ │
│ │ [HttpTrigger] public async Task<IActionResult> │ │
│ │ (ProcessOrderRequest request) │ │
│ │ │ │
│ │ 1. Validate input │ │
│ │ 2. Process business logic │ │
│ │ 3. Call external services │ │
│ │ 4. Return result │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Adding Azure Function Action
Step-by-Step
- Open Logic App in Designer
- Add new action
- Search for "Azure Functions"
- Select "Azure Functions" connector
- Choose your Function App
- Select the function to invoke
- Configure HTTP method and parameters
Basic Configuration
{
"Invoke_Process_Order": {
"type": "AzureFunction",
"inputs": {
"function": {
"appName": "order-processing-app",
"functionName": "ProcessOrder",
"httpMethod": "POST"
},
"body": {
"orderId": "@triggerBody()?['orderId']",
"customerId": "@triggerBody()?['customerId']",
"items": "@triggerBody()?['items']"
}
}
}
}
Authentication Options
Option 1: Function Key (Basic)
{
"Invoke_With_Key": {
"type": "AzureFunction",
"inputs": {
"function": {
"appName": "my-function-app",
"functionName": "MyFunction",
"apiKey": {
"name": "default",
"value": "@appsettings('MyFunctionKey')"
}
},
"method": "POST",
"body": "@triggerBody()"
}
}
}
Option 2: Managed Identity (Recommended)
Step 1: Enable Managed Identity on Logic App
# Enable system-assigned identity
az logicapp update \
--name my-logic-app \
--resource-group my-rg \
--identity systemassigned
# Get principal ID
az logicapp show \
--name my-logic-app \
--resource-group my-rg \
--query "identity.principalId"
Step 2: Enable Authentication on Function
public static class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddAuthentication("Bearer")
.AddMicrosoftIdentityWebApi(
Configuration["AzureAD:Instance"],
Configuration["AzureAD:TenantId"],
Configuration["AzureAD:ClientId"]);
builder.Services.AddAuthorization();
}
}
public class ProcessOrderFunction
{
[FunctionName("ProcessOrder")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req,
ILogger log)
{
// Function is secured with Azure AD
// Logic App uses managed identity to call
var requestBody = await new StreamReader(req.Body).ReadToEndAsync();
// Process...
return new OkObjectResult(result);
}
}
Step 3: Add Function Action
In Logic Apps, the action will automatically use managed identity when no API key is specified.
Passing Data to Functions
Sending JSON Data
{
"body": {
"orderId": "@triggerBody()?['orderId']",
"customer": {
"name": "@triggerBody()?['customerName']",
"email": "@triggerBody()?['customerEmail']"
},
"items": "@triggerBody()?['items']",
"totalAmount": "@triggerBody()?['total']"
}
}
Dynamic Content from Multiple Sources
{
"body": {
"metadata": {
"correlationId": "@guid()",
"timestamp": "@utcNow()",
"source": "logic-app"
},
"order": "@triggerBody()",
"configuration": {
"validateStock": true,
"notifyCustomer": true
}
}
}
Handling Function Responses
Access Response Data
{
"Process_Function_Response": {
"type": "AzureFunction",
"inputs": {
"function": {
"appName": "order-app",
"functionName": "ProcessOrder",
"method": "POST"
},
"body": "@triggerBody()"
},
"runAfter": {}
},
"Check_Result": {
"type": "Condition",
"expression": "@equals(outputs('Process_Function_Response').statusCode, 200)",
"actions": {
"Continue": {
"type": "Response",
"inputs": {
"body": "@outputs('Process_Function_Response').body"
}
}
},
"else": {
"actions": {
"Handle_Error": {
"type": "Terminate",
"inputs": {
"statusCode": "@outputs('Process_Function_Response').statusCode",
"message": "@outputs('Process_Function_Response').body"
}
}
}
}
}
}
Parse Function Response
{
"Get_Order_Result": {
"type": "AzureFunction",
"inputs": {
"appName": "orders-app",
"functionName": "GetOrderDetails",
"method": "GET"
},
"queries": {
"orderId": "@triggerBody()?['orderId']"
}
},
"Extract_Data": {
"type": "ParseJson",
"inputs": {
"content": "@outputs('Get_Order_Result').body"
},
"schema": {
"type": "object",
"properties": {
"orderId": { "type": "string" },
"status": { "type": "string" },
"total": { "type": "number" }
}
}
}
}
Error Handling
Retry on Failure
{
"Invoke_With_Retry": {
"type": "AzureFunction",
"inputs": {
"function": {
"appName": "my-app",
"functionName": "ProcessData"
},
"method": "POST",
"body": "@triggerBody()"
},
"runAfter": {},
"retryPolicy": {
"type": "fixed",
"count": 3,
"interval": "PT5S"
}
}
}
Catch and Handle Errors
{
"Try_Invoke": {
"type": "AzureFunction",
"inputs": { ... },
"runAfter": {}
},
"Catch_Errors": {
"type": "Scope",
"actions": {
"Log_Error": {
"type": "Compose",
"inputs": {
"error": "@outputs('Try_Invoke').error.message",
"timestamp": "@utcNow()"
}
},
"Notify_Team": {
"type": "SendEmail",
"inputs": {
"body": "Function call failed: @{outputs('Try_Invoke').error.message}"
}
}
}
}
}
Real-World Patterns
Pattern 1: Complex Business Logic
┌─────────────────────────────────────────────────────┐
│ COMPLEX ORDER PROCESSING │
├─────────────────────────────────────────────────────┤
│ │
│ 1. HTTP Trigger (new order) │
│ 2. Validate Order (Logic Apps) │
│ 3. Invoke Function: Calculate Total │
│ - Apply discounts │
│ - Calculate tax │
│ - Determine shipping │
│ 4. Invoke Function: Reserve Inventory │
│ - Check stock levels │
│ - Reserve items │
│ 5. Save to Database (Logic Apps) │
│ 6. Send confirmation (Logic Apps) │
│ │
└─────────────────────────────────────────────────────┘
Pattern 2: Data Transformation
{
"Transform_Data": {
"type": "AzureFunction",
"inputs": {
"appName": "data-transformer",
"functionName": "TransformOrder",
"method": "POST"
},
"body": {
"inputFormat": "legacy",
"outputFormat": "modern",
"data": "@triggerBody()"
}
}
}
Pattern 3: External API Integration
{
"Call_External_API": {
"type": "AzureFunction",
"inputs": {
"appName": "api-integration",
"functionName": "SyncWithCRM",
"method": "POST"
},
"body": {
"customerId": "@triggerBody()?['customerId']",
"action": "update",
"data": "@triggerBody()"
}
}
}
Best Practices
| Practice | Description |
|---|---|
| Use Managed Identity | Avoid storing API keys |
| Design for failure | Implement retry and error handling |
| Keep functions focused | Single responsibility principle |
| Use proper logging | Track function invocations |
| Version functions | Use function app versioning |
Performance Considerations
- Use async/await in functions for long operations
- Consider function timeout settings
- Batch multiple operations when possible
- Cache function app warm
Related Topics
- Storage Connector — Azure Storage integration
- Service Bus Connector — Message-based triggers
- Azure Functions — Function fundamentals
Azure Integration Hub - Intermediate Level