← Back to DevBytes

Logic Apps: Complete Setup and Configuration Guide

Introduction to Azure Logic Apps

Azure Logic Apps is a cloud-based platform as a service (PaaS) that enables developers and organizations to automate workflows, integrate applications, data sources, and services across cloud and on-premises environments. Logic Apps provide a visual designer to build scalable integrations and automations with minimal code, while also supporting advanced expression-based logic and custom API calls.

At its core, a Logic App is a workflow definition expressed in JSON, executed by the Azure Logic Apps engine. Each workflow begins with a trigger—an event that starts the workflow—and continues with one or more actions that perform operations such as sending emails, calling REST APIs, transforming data, or executing conditional branches.

Why Logic Apps Matter

🚀 Deploy your AI agent in 10 minutes

Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.

Try it free →

Logic Apps solve several critical challenges in modern enterprise integration:

Core Concepts and Terminology

Before diving into setup, understanding these core concepts will help you navigate the Logic Apps ecosystem effectively:

Logic App Types: Consumption vs. Standard

Azure offers two hosting models for Logic Apps, and choosing the right one is fundamental:

Consumption Logic Apps

Consumption Logic Apps run in a multi-tenant, serverless environment managed entirely by Azure. They scale out instantly and bill per execution and per action. This model is ideal for event-driven, low-to-medium volume integrations where cost optimization is paramount.

Standard Logic Apps

Standard Logic Apps run on a dedicated, single-tenant Azure Functions-based runtime, giving you more control over networking, scaling behavior, and compute isolation. They can be deployed in App Service Environments (ASE) with private networking. Standard Logic Apps bill based on the App Service Plan, making them suitable for high-volume, predictable workloads or scenarios requiring advanced networking security.

Step-by-Step: Creating Your First Logic App

Let's walk through creating a complete Logic App that monitors an email inbox for support requests, creates a ticket in a tracking system, and sends a confirmation. We'll use the Azure Portal, Azure CLI, and ARM template approaches to give you full flexibility.

Approach 1: Azure Portal Visual Designer

This is the quickest way to prototype and build workflows:

  1. Navigate to the Azure Portal and search for "Logic Apps" in the top search bar.
  2. Click Create and select Consumption as the resource type.
  3. Fill in the required fields: Resource Group, Logic App Name, Region, and optionally enable log analytics for monitoring.
  4. Click Review + Create, then Create to provision the resource.
  5. Once deployed, open the Logic App resource and select Logic App Designer from the left menu.
  6. Choose a trigger template. For this example, select When a new email arrives (Office 365 Outlook).
  7. Authenticate to Office 365 and configure the trigger: set the folder to "Inbox" and add a subject filter like "Support Request."
  8. Click New Step to add an action. Search for "Create a SharePoint item" and configure it to log each support email into a SharePoint list.
  9. Add another action: Send an email (Outlook) to send a confirmation reply to the sender.
  10. Save the workflow. Your Logic App is now live and will execute on every matching email.

Approach 2: Azure CLI

For automation and CI/CD pipelines, use the Azure CLI to create and manage Logic Apps programmatically:

#!/bin/bash
# Login to Azure
az login

# Set the target subscription
az account set --subscription "your-subscription-id"

# Create a resource group
az group create \
  --name "rg-logicapps-demo" \
  --location "westeurope"

# Create a Consumption Logic App
az logic workflow create \
  --resource-group "rg-logicapps-demo" \
  --name "SupportTicketWorkflow" \
  --location "westeurope" \
  --definition workflow-definition.json

# Enable diagnostics logging
az monitor diagnostic-settings create \
  --resource "/subscriptions/sub-id/resourceGroups/rg-logicapps-demo/providers/Microsoft.Logic/workflows/SupportTicketWorkflow" \
  --name "sendToLogAnalytics" \
  --workspace "/subscriptions/sub-id/resourceGroups/rg-monitoring/providers/Microsoft.OperationalInsights/workspaces/central-logs" \
  --logs '[{"category":"WorkflowRuntime","enabled":true}]'

# List recent runs
az logic workflow run list \
  --resource-group "rg-logicapps-demo" \
  --workflow-name "SupportTicketWorkflow" \
  --max-items 10 \
  -o table

Approach 3: ARM Template (Infrastructure as Code)

For production deployments, define your Logic App as an ARM template for repeatable, version-controlled infrastructure:

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "workflowName": {
      "type": "string",
      "defaultValue": "SupportTicketWorkflow"
    },
    "location": {
      "type": "string",
      "defaultValue": "[resourceGroup().location]"
    }
  },
  "resources": [
    {
      "type": "Microsoft.Logic/workflows",
      "apiVersion": "2019-05-01",
      "name": "[parameters('workflowName')]",
      "location": "[parameters('location')]",
      "properties": {
        "state": "Enabled",
        "definition": {
          "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
          "contentVersion": "1.0.0.0",
          "parameters": {},
          "triggers": {
            "When_a_new_email_arrives": {
              "type": "ApiConnection",
              "inputs": {
                "host": {
                  "connection": {
                    "name": "@parameters('$connections')['office365']['connectionId']"
                  }
                },
                "method": "get",
                "path": "/v2/OnNewEmail",
                "queries": {
                  "folder": "Inbox",
                  "subjectFilter": "Support Request",
                  "importance": "Normal"
                }
              },
              "recurrence": {
                "frequency": "Minute",
                "interval": 3
              }
            }
          },
          "actions": {
            "Create_SharePoint_Item": {
              "type": "ApiConnection",
              "inputs": {
                "host": {
                  "connection": {
                    "name": "@parameters('$connections')['sharepointonline']['connectionId']"
                  }
                },
                "method": "post",
                "path": "/v1/datasets/@{encodeURIComponent(encodeURIComponent('https://contoso.sharepoint.com/sites/Support'))}/tables/@{encodeURIComponent(encodeURIComponent('SupportTickets'))}/items",
                "body": {
                  "Title": "@triggerBody()?['subject']",
                  "Requester": "@triggerBody()?['from']",
                  "Description": "@triggerBody()?['body']"
                }
              }
            },
            "Send_confirmation_email": {
              "type": "ApiConnection",
              "inputs": {
                "host": {
                  "connection": {
                    "name": "@parameters('$connections')['office365']['connectionId']"
                  }
                },
                "method": "post",
                "path": "/v2/Mail",
                "body": {
                  "to": "@triggerBody()?['from']",
                  "subject": "We received your support request",
                  "body": "Thank you for contacting support. Your ticket has been created."
                }
              },
              "runAfter": {
                "Create_SharePoint_Item": ["Succeeded"]
              }
            }
          }
        },
        "parameters": {
          "$connections": {
            "value": {
              "office365": {
                "connectionId": "/subscriptions/sub-id/resourceGroups/rg-logicapps-demo/providers/Microsoft.Web/connections/office365",
                "connectionName": "office365",
                "id": "/subscriptions/sub-id/providers/Microsoft.Web/locations/westeurope/managedApis/office365"
              },
              "sharepointonline": {
                "connectionId": "/subscriptions/sub-id/resourceGroups/rg-logicapps-demo/providers/Microsoft.Web/connections/sharepointonline",
                "connectionName": "sharepointonline",
                "id": "/subscriptions/sub-id/providers/Microsoft.Web/locations/westeurope/managedApis/sharepointonline"
              }
            }
          }
        }
      }
    }
  ]
}

Deploy this template using Azure CLI:

az deployment group create \
  --resource-group "rg-logicapps-demo" \
  --template-file logicapp-template.json \
  --parameters workflowName=SupportTicketWorkflow

Working with Workflow Definition Language

Every Logic App workflow is defined using a JSON-based language. Understanding this format unlocks advanced capabilities and allows you to version-control your workflows in source repositories.

Trigger Configuration

Triggers define how your workflow starts. Here are common trigger patterns:

{
  "triggers": {
    "Recurrence_Schedule": {
      "type": "Recurrence",
      "recurrence": {
        "frequency": "Day",
        "interval": 1,
        "schedule": {
          "hours": ["09"],
          "minutes": ["00"],
          "timeZone": "Pacific Standard Time"
        }
      }
    }
  }
}

This trigger fires every weekday at 9:00 AM Pacific Time. The schedule property allows precise control over execution timing.

Conditional Logic and Switch Statements

Use conditions to branch workflow execution based on data values:

{
  "actions": {
    "Check_Priority": {
      "type": "Switch",
      "expression": "@triggerBody()?['priority']",
      "cases": {
        "High": {
          "actions": {
            "Escalate_to_Manager": {
              "type": "ApiConnection",
              "inputs": {
                "host": { "connection": { "name": "@parameters('$connections')['office365']['connectionId']" } },
                "method": "post",
                "path": "/v2/Mail",
                "body": {
                  "to": "manager@contoso.com",
                  "subject": "High Priority Escalation",
                  "body": "@triggerBody()?['description']"
                }
              }
            }
          }
        },
        "Low": {
          "actions": {
            "Log_to_Database": {
              "type": "ApiConnection",
              "inputs": {
                "host": { "connection": { "name": "@parameters('$connections')['sql']['connectionId']" } },
                "method": "post",
                "path": "/v2/execute-stored-procedure",
                "body": {
                  "procedureName": "sp_LogLowPriority",
                  "parameters": { "description": "@triggerBody()?['description']" }
                }
              }
            }
          }
        }
      },
      "default": {
        "actions": {
          "Send_Notification": {
            "type": "ApiConnection",
            "inputs": {
              "host": { "connection": { "name": "@parameters('$connections')['office365']['connectionId']" } },
              "method": "post",
              "path": "/v2/Mail",
              "body": {
                "to": "team@contoso.com",
                "subject": "New Ticket - No Priority Set",
                "body": "A ticket was created without a priority assignment."
              }
            }
          }
        }
      }
    }
  }
}

Loops: For Each and Until

Process collections of items or implement retry logic with loops:

{
  "actions": {
    "Process_Order_Items": {
      "type": "Foreach",
      "foreach": "@triggerBody()?['lineItems']",
      "actions": {
        "Validate_Inventory": {
          "type": "ApiConnection",
          "inputs": {
            "host": { "connection": { "name": "@parameters('$connections')['sql']['connectionId']" } },
            "method": "post",
            "path": "/v2/execute-stored-procedure",
            "body": {
              "procedureName": "sp_CheckInventory",
              "parameters": {
                "sku": "@item()?['sku']",
                "quantity": "@item()?['quantity']"
              }
            }
          }
        }
      },
      "runAfter": { "Parse_Order": ["Succeeded"] }
    },
    "Wait_for_Approval": {
      "type": "Until",
      "expression": "@equals(variables('approvalStatus'), 'approved')",
      "actions": {
        "Check_Status": {
          "type": "Http",
          "inputs": {
            "method": "GET",
            "uri": "https://approval-system.contoso.com/api/status/@{variables('approvalId')}"
          }
        },
        "Update_Status_Variable": {
          "type": "SetVariable",
          "inputs": {
            "name": "approvalStatus",
            "value": "@body('Check_Status')?['status']"
          }
        },
        "Delay": {
          "type": "Wait",
          "inputs": {
            "interval": {
              "count": 30,
              "unit": "Second"
            }
          }
        }
      },
      "limit": {
        "count": 60,
        "timeout": "PT1H"
      }
    }
  }
}

The Until loop polls an approval system every 30 seconds for up to 60 iterations or 1 hour, whichever comes first. This pattern is perfect for long-running human-in-the-loop processes.

Variables and Expressions

Logic Apps support variables for state management and a rich expression language for data manipulation:

{
  "actions": {
    "Initialize_Counters": {
      "type": "InitializeVariable",
      "inputs": {
        "variables": [
          {
            "name": "processedCount",
            "type": "Integer",
            "value": 0
          },
          {
            "name": "errorList",
            "type": "Array",
            "value": []
          },
          {
            "name": "currentTimestamp",
            "type": "String",
            "value": "@{utcNow()}"
          }
        ]
      }
    },
    "Format_Date_For_Report": {
      "type": "Compose",
      "inputs": {
        "formattedDate": "@{formatDateTime(triggerBody()?['eventDate'], 'yyyy-MM-dd')}",
        "fiscalQuarter": "@{concat('Q', string(add(div(sub(int(lastIndexOf(string(triggerBody()?['eventDate']), '-'))), 3), 1)))}"
      },
      "runAfter": { "Initialize_Counters": ["Succeeded"] }
    },
    "Increment_Counter": {
      "type": "IncrementVariable",
      "inputs": {
        "name": "processedCount",
        "value": 1
      },
      "runAfter": { "Process_Item": ["Succeeded"] }
    }
  }
}

Common expression patterns include:

Connectors and API Connections

Connectors are the bridge between your Logic App and external services. They fall into two categories:

Managed Connectors

Managed connectors are hosted and managed by Azure. They include services like Office 365, Salesforce, Dynamics 365, and Azure services. Authentication is handled through Azure AD or OAuth, and you create API connections once per account per region.

Built-in Connectors

Built-in connectors run directly within the Logic Apps runtime, offering lower latency and no external dependency. Examples include Azure Functions, Service Bus, Event Grid, and the HTTP connector for calling arbitrary REST endpoints.

Creating a Custom HTTP Connector Action

When no pre-built connector exists for your service, use the HTTP action to call any REST API:

{
  "actions": {
    "Call_External_Weather_API": {
      "type": "Http",
      "inputs": {
        "method": "GET",
        "uri": "https://api.weatherprovider.com/v3/forecast",
        "headers": {
          "Authorization": "Bearer @{variables('apiToken')}",
          "Accept": "application/json"
        },
        "queries": {
          "latitude": "@triggerBody()?['location']?['lat']",
          "longitude": "@triggerBody()?['location']?['lng']",
          "units": "metric"
        },
        "authentication": {
          "type": "Raw",
          "value": "@variables('apiToken')"
        }
      },
      "retryPolicy": {
        "type": "Exponential",
        "interval": "PT10S",
        "count": 3,
        "minimumInterval": "PT5S",
        "maximumInterval": "PT1M"
      }
    },
    "Parse_Weather_Response": {
      "type": "ParseJson",
      "inputs": {
        "content": "@body('Call_External_Weather_API')",
        "schema": {
          "type": "object",
          "properties": {
            "temperature": { "type": "number" },
            "conditions": { "type": "string" },
            "humidity": { "type": "number" }
          }
        }
      },
      "runAfter": { "Call_External_Weather_API": ["Succeeded"] }
    }
  }
}

The retryPolicy configuration implements exponential backoff, retrying up to 3 times with increasing delays. This is critical for resilient cloud integrations.

Error Handling and Scoped Execution

Production workflows must handle failures gracefully. Logic Apps provide scopes and error handling actions:

{
  "actions": {
    "Try_Block": {
      "type": "Scope",
      "actions": {
        "Process_Payment": {
          "type": "ApiConnection",
          "inputs": {
            "host": { "connection": { "name": "@parameters('$connections')['stripe']['connectionId']" } },
            "method": "post",
            "path": "/v1/charges",
            "body": {
              "amount": "@triggerBody()?['amount']",
              "currency": "usd",
              "source": "@triggerBody()?['token']"
            }
          }
        },
        "Update_Order_Status": {
          "type": "ApiConnection",
          "inputs": {
            "host": { "connection": { "name": "@parameters('$connections')['sql']['connectionId']" } },
            "method": "post",
            "path": "/v2/execute-stored-procedure",
            "body": {
              "procedureName": "sp_UpdateOrderStatus",
              "parameters": {
                "orderId": "@triggerBody()?['orderId']",
                "status": "paid"
              }
            }
          },
          "runAfter": { "Process_Payment": ["Succeeded"] }
        }
      },
      "runAfter": {}
    },
    "Catch_Block": {
      "type": "Scope",
      "actions": {
        "Log_Failure": {
          "type": "ApiConnection",
          "inputs": {
            "host": { "connection": { "name": "@parameters('$connections')['azureloganalytics']['connectionId']" } },
            "method": "post",
            "path": "/v2/sendData",
            "body": {
              "eventType": "PaymentFailure",
              "orderId": "@triggerBody()?['orderId']",
              "error": "@result('Try_Block')?['error']",
              "timestamp": "@{utcNow()}"
            }
          }
        },
        "Notify_Support": {
          "type": "ApiConnection",
          "inputs": {
            "host": { "connection": { "name": "@parameters('$connections')['office365']['connectionId']" } },
            "method": "post",
            "path": "/v2/Mail",
            "body": {
              "to": "support@contoso.com",
              "subject": "Payment Processing Failure",
              "body": "Order @{triggerBody()?['orderId']} failed payment processing. Error: @{result('Try_Block')?['error']}"
            }
          },
          "runAfter": { "Log_Failure": ["Succeeded"] }
        },
        "Set_Order_Status_Failed": {
          "type": "ApiConnection",
          "inputs": {
            "host": { "connection": { "name": "@parameters('$connections')['sql']['connectionId']" } },
            "method": "post",
            "path": "/v2/execute-stored-procedure",
            "body": {
              "procedureName": "sp_UpdateOrderStatus",
              "parameters": {
                "orderId": "@triggerBody()?['orderId']",
                "status": "payment_failed"
              }
            }
          },
          "runAfter": { "Notify_Support": ["Succeeded"] }
        }
      },
      "runAfter": { "Try_Block": ["Failed", "TimedOut"] }
    }
  }
}

The runAfter property on Catch_Block specifies that it executes only when Try_Block fails or times out. This try-catch pattern ensures that failures are always handled and logged, preventing silent data loss.

On-Premises Connectivity with Data Gateway

Many enterprises need Logic Apps to reach systems behind firewalls. The on-premises data gateway enables secure, outbound-only connectivity to resources like SQL Server, file shares, SAP, and Oracle databases.

Setting Up the Gateway

  1. Download the on-premises data gateway installer from the Azure portal.
  2. Install it on a server within your network that has line-of-sight to the target systems. The server must run Windows and remain powered on continuously.
  3. During installation, sign in with your Azure account and register the gateway with a unique name.
  4. In the Azure Portal, navigate to your Logic App, open the Resource menu, and select Gateways. Create a connection resource referencing your gateway.

Connecting to On-Premises SQL Server

Once the gateway is registered, create an API connection and use it in your workflow:

{
  "actions": {
    "Query_OnPrem_Inventory": {
      "type": "ApiConnection",
      "inputs": {
        "host": {
          "connection": {
            "name": "@parameters('$connections')['sql']['connectionId']"
          }
        },
        "method": "get",
        "path": "/v2/datasets/@{encodeURIComponent(encodeURIComponent('OnPremSQLServer'))}/tables/@{encodeURIComponent(encodeURIComponent('dbo.Inventory'))}/items",
        "queries": {
          "$filter": "Quantity lt 10",
          "$top": 100
        }
      }
    },
    "Insert_Replenishment_Order": {
      "type": "ApiConnection",
      "inputs": {
        "host": {
          "connection": {
            "name": "@parameters('$connections')['sql']['connectionId']"
          }
        },
        "method": "post",
        "path": "/v2/datasets/@{encodeURIComponent(encodeURIComponent('OnPremSQLServer'))}/tables/@{encodeURIComponent(encodeURIComponent('dbo.ReplenishmentOrders'))}/items",
        "body": {
          "ProductId": "@item()?['ProductId']",
          "Quantity": 50,
          "OrderDate": "@{utcNow()}"
        }
      },
      "runAfter": { "Query_OnPrem_Inventory": ["Succeeded"] }
    }
  }
}

Monitoring, Logging, and Alerting

Observability is essential for production Logic Apps. Azure provides multiple layers of monitoring:

Azure Monitor and Log Analytics

Enable diagnostic settings to send workflow run data to Log Analytics for querying:

# Enable diagnostic settings via CLI
az monitor diagnostic-settings create \
  --resource "/subscriptions/sub-id/resourceGroups/rg-logicapps-demo/providers/Microsoft.Logic/workflows/SupportTicketWorkflow" \
  --name "workflowLogs" \
  --workspace "/subscriptions/sub-id/resourceGroups/rg-monitoring/providers/Microsoft.OperationalInsights/workspaces/central-logs" \
  --logs '[
    {"category": "WorkflowRuntime", "enabled": true},
    {"category": "WorkflowTrigger", "enabled": true}
  ]' \
  --metrics '[
    {"category": "AllMetrics", "enabled": true}
  ]'

Common Kusto queries for monitoring:

// Find all failed runs in the last 24 hours
AzureDiagnostics
| where ResourceType == "WORKFLOWS"
| where status == "Failed"
| where TimeGenerated > ago(24h)
| project TimeGenerated, resource_workflowName_s, status, error_code_s, error_message_s
| order by TimeGenerated desc

// Calculate success rate by workflow
AzureDiagnostics
| where ResourceType == "WORKFLOWS"
| summarize Total = count(), 
            Succeeded = countif(status == "Succeeded"),
            Failed = countif(status == "Failed")
            by resource_workflowName_s, bin(TimeGenerated, 1d)
| extend SuccessRate = round(Succeeded * 100.0 / Total, 2)
| project TimeGenerated, resource_workflowName_s, Total, Succeeded, Failed, SuccessRate
| order by TimeGenerated desc

// Identify the slowest actions across all workflows
AzureDiagnostics
| where ResourceType == "WORKFLOWS" and category == "WorkflowRuntime"
| extend durationSeconds = toreal(executionDurationMs) / 1000
| where durationSeconds > 30
| project TimeGenerated, resource_workflowName_s, actionName_s, durationSeconds
| order by durationSeconds desc

Setting Up Alerts

Create alerts for workflow failures to notify your operations team:

# Create an action group for email notification
az monitor action-group create \
  --resource-group "rg-monitoring" \
  --name "LogicAppFailureAlertGroup" \
  --action email ops-team ops@contoso.com

# Create a metric alert for workflow failures
az monitor metrics alert create \
  --resource-group "rg-monitoring" \
  --name "WorkflowFailureAlert" \
  --scopes "/subscriptions/sub-id/resourceGroups/rg-logicapps-demo/providers/Microsoft.Logic/workflows/SupportTicketWorkflow" \
  --condition "total RunsFailed > 0" \
  --window-size 5m \
  --evaluation-frequency 1m \
  --action "/subscriptions/sub-id/resourceGroups/rg-monitoring/providers/Microsoft.Insights/actionGroups/LogicAppFailureAlertGroup" \
  --description "Alert when any workflow run fails"

Security and Access Control

Managed Identities

Instead of managing credentials, use Managed Identities to authenticate Logic Apps to Azure services:

# Enable system-assigned managed identity
az logic workflow identity assign \
  --resource-group "rg-logicapps-demo" \
  --name "SupportTicketWorkflow" \
  --identity-type SystemAssigned

# Grant the identity access to Azure Key Vault secrets
az keyvault set-policy \
  --name "contoso-keyvault" \
  --object-id "$(az logic workflow show --resource-group rg-logicapps-demo --name SupportTicketWorkflow --query identity.principalId -o tsv)" \
  --secret-permissions get list

In your workflow definition, reference Key Vault secrets securely:

{
  "actions": {
    "Get_Database_Password": {
      "type": "Compose",
      "inputs": {
        "secret": "@{json(concat('{\"uri\":\"https://contoso-keyvault.vault.azure.net/secrets/database-password\"}'))}"
      },
      "managedIdentity": {
        "type": "SystemAssigned"
      }
    }
  }
}

RBAC and Least Privilege

Assign granular roles to control who can edit, trigger, or view Logic Apps:

# Grant developer team contributor access at resource group level
az role assignment create \
  --assignee "dev-team-group-object-id" \
  --role "Logic App Contributor" \
  --resource-group "rg-logicapps-demo"

# Grant operations team operator access to a specific workflow
az role assignment create \
  --assignee "ops-team-group-object-id" \
  --role "Logic App Operator" \
  --scope "/subscriptions/sub-id/resourceGroups/rg-logicapps-demo/providers/Microsoft.Logic/workflows/SupportTicketWorkflow"

Best Practices for Production Logic Apps

Concurrency Configuration Example

{
  "triggers": {
    "ServiceBus_Queue_Trigger": {
      "type": "ApiConnection",
      "inputs": {

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles