01What Is Azure Logic Apps?
Azure Logic Apps is a cloud-based integration platform (iPaaS) that lets you automate workflows, connect services, and orchestrate business processes — visually or via code. With 500+ built-in connectors, you can integrate Azure services, SaaS platforms (Salesforce, Office 365, Dynamics), on-premises systems, and custom APIs without writing infrastructure code.
02Core Concepts & Terminology
Before building workflows, it helps to understand the foundational vocabulary that Logic Apps uses throughout its designer, JSON definitions, and documentation. These concepts map directly to how you structure, debug, and monitor your integrations in production. Mastering them early prevents confusion when reading run histories or troubleshooting connector failures. Think of each term as a building block — triggers start things, actions do things, connectors connect things, and run history shows you what happened.
| Term | Definition |
|---|---|
| Workflow | A series of steps (trigger + actions) that execute in sequence or parallel |
| Trigger | The event that starts a workflow — HTTP request, timer, queue message, blob created, etc. |
| Action | A step in the workflow — call API, send email, write to DB, transform data, etc. |
| Connector | A pre-built adapter for a specific service (Azure Storage, Outlook, SQL, Salesforce) |
| Run History | A log of every workflow execution — inputs, outputs, duration, status of each step |
| Managed Connector | Microsoft-hosted, auto-scaled connector infrastructure |
| Built-in Connector | Runs in-process with the Logic App runtime — faster, VNet-aware, cheaper |
| Integration Account | Container for B2B artifacts: schemas (XSD), maps (XSLT), certificates, partners |
| ISE (deprecated) | Integration Service Environment — now replaced by Standard Logic Apps + VNet |
| Designer | Visual workflow editor in Azure Portal or VS Code |
03Consumption vs Standard
Azure Logic Apps offers two hosting models that differ significantly in pricing, networking, performance, and deployment flexibility. Choosing the right plan upfront is critical because migrating between them later requires rebuilding workflows. Consumption is ideal for low-volume, simple integrations where you pay only for what you use, while Standard unlocks VNet isolation, multiple workflows per app, stateless execution, and full CI/CD support for enterprise production workloads. Evaluate your throughput, security, and DevOps requirements before committing to a plan.
| Feature | Consumption (Multi-tenant) | Standard (Single-tenant) |
|---|---|---|
| Hosting | Shared multi-tenant | Dedicated single-tenant |
| Workflows per app | 1 workflow | Multiple workflows |
| Pricing | Pay per action execution | Fixed vCPU + memory/hr |
| Stateful workflows | ✓ Yes | ✓ Yes |
| Stateless workflows | ✗ No | ✓ Yes |
| VNet Integration | ✗ No | ✓ Yes |
| Private Endpoints | ✗ No | ✓ Yes |
| Built-in connectors | Limited | Expanded (Service Bus, Storage, HTTP native) |
| Custom code | ✗ No | ✅ inline C# scripts |
| Local development | Limited | ✅ VS Code + Azurite emulator |
| CI/CD friendly | Via ARM/Bicep | ✅ Full file-based deployment |
| B2B / EDI | Integration Account | Integration Account |
| SLA | 99.9% | 99.95% |
| Deployment | Azure Portal / ARM | App Service Plan / Zip deploy |
04Triggers — All Types
Triggers are the entry point of every Logic App workflow — they define what event starts execution. Understanding trigger types is essential because they directly impact latency, cost, and reliability of your automation. Push-based triggers (webhooks, Event Grid) deliver near-instant response times, while polling triggers check for new data on an interval and incur costs per poll. Choose your trigger type based on how time-sensitive your workflow is and whether the source system supports push notifications.
Trigger Categories
| Category | Examples | How It Works |
|---|---|---|
| HTTP / Webhook | When HTTP request received | Logic App exposes a URL; caller POSTs to it |
| Schedule / Recurrence | Recurrence trigger, Sliding Window | Fires on a cron schedule or interval |
| Polling | Service Bus queue, Blob Storage, SQL, Outlook | Logic Apps polls the source on an interval |
| Push / Event | Event Grid, Event Hub, Service Bus push | Source pushes events to Logic App webhook |
| Manual | Manual trigger | Only fires when you click 'Run' in Portal |
HTTP Trigger (Webhook)
{
"triggers": {
"manual": {
"type": "Request",
"kind": "Http",
"inputs": {
"schema": {
"type": "object",
"properties": {
"orderId": { "type": "string" },
"amount": { "type": "number" },
"region": { "type": "string" }
},
"required": ["orderId", "amount"]
}
}
}
}
}Recurrence Trigger
{
"triggers": {
"Recurrence": {
"type": "Recurrence",
"recurrence": {
"frequency": "Hour",
"interval": 1,
"startTime": "2025-01-01T00:00:00Z",
"timeZone": "UTC",
"schedule": {
"hours": ["6", "12", "18"],
"minutes": [0, 30]
}
}
}
}
}Service Bus Trigger (Polling)
{
"triggers": {
"When_a_message_is_received_in_a_queue": {
"type": "ApiConnection",
"inputs": {
"host": { "connection": { "name": "@parameters('$connections')['servicebus']['connectionId']" } },
"method": "get",
"path": "/@{encodeURIComponent('orders')}/messages/head/peek",
"queries": { "maxMessageCount": 1 }
},
"recurrence": { "frequency": "Second", "interval": 30 }
}
}
}Trigger Polling Interval Guide
| Trigger Type | Min Poll Interval | Notes |
|---|---|---|
| Service Bus (Consumption) | 30 seconds | Built-in (Standard) is push-based — much faster |
| Blob Storage | 1 minute | Use Event Grid trigger for near-real-time |
| SQL Server | 1 minute | Polling adds load to DB — prefer change feed |
| Outlook / SharePoint | 1 minute | M365 connector rate limits apply |
| HTTP Webhook | Instant | Source calls Logic App directly — no polling |
| Event Grid | Instant | Push-based — ideal for event-driven architectures |
05Actions & Control Flow
Actions are the individual steps that execute after a trigger fires — they call APIs, transform data, send notifications, and control the flow of your workflow. Control flow actions like conditions, loops, and scopes let you build complex branching logic without writing code. In production, proper use of scopes and run-after configurations is what separates fragile workflows from resilient ones. Master these building blocks and you can orchestrate any business process, from simple notifications to multi-step approval chains with error recovery.
Control Flow Actions
| Action | Description |
|---|---|
| Condition (If/Else) | Branch workflow based on a boolean expression |
| Switch | Multi-branch routing based on expression value |
| For Each | Loop over array items — parallel by default (up to 20 concurrent) |
| Until | Loop until a condition becomes true (max iterations + timeout) |
| Scope | Group actions into a named block for error handling |
| Terminate | End the workflow run immediately with Succeeded / Failed / Cancelled |
| Delay / Delay Until | Pause execution for a duration or until a specific time |
| Initialize Variable | Create a workflow-scoped variable |
| Set Variable | Update an existing variable |
| Append to Array/String | Add to a collection variable |
| Parse JSON | Parse a JSON string into a typed object for use in expressions |
| Compose | Evaluate and store an expression result without an API call |
| HTTP | Call any REST API — GET, POST, PUT, DELETE, PATCH |
| Response | Return an HTTP response to the HTTP trigger caller |
Condition Action
{
"Condition": {
"type": "If",
"expression": {
"and": [
{ "greater": ["@triggerBody()?['amount']", 1000] },
{ "equals": ["@triggerBody()?['region']", "EU"] }
]
},
"actions": {
"Send_approval_email": { "..." : "..." }
},
"else": {
"actions": {
"Auto_approve": { "..." : "..." }
}
}
}
}For Each (Parallel Loop)
{
"For_each_order": {
"type": "Foreach",
"foreach": "@body('Get_orders')?['value']",
"runtimeConfiguration": {
"concurrency": { "repetitions": 5 }
},
"actions": {
"Process_order": { "..." : "..." }
}
}
}HTTP Action — Call External API
{
"Call_backend_API": {
"type": "Http",
"inputs": {
"method": "POST",
"uri": "https://api.mybackend.com/v1/orders",
"headers": {
"Content-Type": "application/json",
"X-Correlation-Id": "@{triggerOutputs()?['headers']?['X-Request-Id']}"
},
"body": {
"orderId": "@triggerBody()?['orderId']",
"amount": "@triggerBody()?['amount']",
"processedAt": "@utcNow()"
},
"authentication": {
"type": "ManagedServiceIdentity",
"audience": "api://my-backend-api"
}
},
"runAfter": {}
}
}06Connectors — Built-in & Managed
Run in-process with Logic App runtime. Faster, cheaper, VNet-aware, no separate connection resource.
- HTTP / HTTP + Swagger
- Azure Service Bus (built-in)
- Azure Blob Storage (built-in)
- Azure Event Hubs (built-in)
- SQL Server (built-in)
- Schedule / Recurrence
- Request / Response
- Inline Code (C# / JS)
- Liquid / XSLT transforms
- Flat File encode/decode
Microsoft-hosted, auto-scaled. Separate connection resource in Azure. 500+ available.
- Office 365 / Outlook
- SharePoint Online
- Salesforce / Dynamics 365
- SAP (on-premises gateway)
- Twilio / SendGrid
- Dropbox / OneDrive / Box
- GitHub / Azure DevOps
- Twitter / LinkedIn
- ServiceNow / Zendesk
- Stripe / PayPal
On-Premises Gateway Connectors
For on-premises data sources, install the On-Premises Data Gateway on a local machine. Supported: SQL Server, Oracle, MySQL, PostgreSQL, SAP, SharePoint on-premises, BizTalk, file system.
07Expressions & Functions
Expressions are the formula language of Logic Apps — they let you access trigger data, transform strings, perform calculations, and make decisions without writing custom code. Every dynamic value you see in the designer (green tokens) is an expression under the hood. In production workflows, mastering null-safe property access and coalesce patterns prevents runtime failures when upstream data is incomplete. Use expressions liberally in Compose actions to build intermediate payloads, making your workflows easier to debug and maintain.
Expression Syntax
@{expression} — String interpolation inside a string
@expression — Dynamic value (top-level field value)
@{...}@{...} — Multiple expressions in one stringCommonly Used Functions
| Category | Function | Example |
|---|---|---|
| Trigger/Action data | triggerBody() | @triggerBody()?['orderId'] |
| Trigger/Action data | body('Action_Name') | @body('Get_order')?['status'] |
| Trigger/Action data | outputs('Action_Name') | @outputs('HTTP_call')['statusCode'] |
| String | concat() | @concat('Order-', triggerBody()?['id']) |
| String | toUpper() / toLower() | @toUpper(triggerBody()?['region']) |
| String | substring() | @substring(variables('str'), 0, 10) |
| String | replace() | @replace(variables('text'), 'old', 'new') |
| String | split() | @split(triggerBody()?['tags'], ',') |
| Date/Time | utcNow() | @utcNow('yyyy-MM-ddTHH:mm:ssZ') |
| Date/Time | addHours() / addDays() | @addHours(utcNow(), 24) |
| Date/Time | formatDateTime() | @formatDateTime(utcNow(), 'dd/MM/yyyy') |
| Array | length() | @length(body('List_items')?['value']) |
| Array | first() / last() | @first(body('List_items')?['value']) |
| Array | union() / intersection() | @union(variables('arr1'), variables('arr2')) |
| JSON | json() | @json(body('HTTP_call')) |
| JSON | string() | @string(variables('myObject')) |
| Math | add() / sub() / mul() / div() | @add(triggerBody()?['price'], 10) |
| Logic | if() | @if(equals(variables('x'),1),'yes','no') |
| Logic | equals() / greater() / less() | @equals(triggerBody()?['type'], 'premium') |
| Encoding | base64() / base64ToString() | @base64(variables('secret')) |
| GUID | guid() | @guid() |
Null-Safe Property Access
// Use ?. to safely access nested properties (returns null instead of error)
@triggerBody()?['order']?['customer']?['email']
// Provide default with coalesce
@coalesce(triggerBody()?['region'], 'US')
// Check if property exists
@contains(triggerBody(), 'optionalField')08Error Handling & Retry
Robust error handling separates production-grade workflows from fragile prototypes. Logic Apps provides run-after configuration, scope-based try/catch patterns, and configurable retry policies to handle transient failures gracefully. Without proper error handling, a single failed HTTP call can leave messages unprocessed, data inconsistent, or customers unnotified. Always wrap risky actions in scopes, configure exponential retry for external API calls, and use run-after to route failures to logging or dead-letter handling paths.
Run After Configuration
Every action has a runAfter property controlling when it executes based on the previous action's status.
{
"Handle_error": {
"type": "...",
"runAfter": {
"Process_order": ["Failed", "TimedOut", "Skipped"]
}
},
"Continue_on_success": {
"runAfter": {
"Process_order": ["Succeeded"]
}
}
}Scope + Run After (Try/Catch Pattern)
{
"Try_scope": {
"type": "Scope",
"actions": {
"Risky_action": { "..." : "..." }
}
},
"Catch_scope": {
"type": "Scope",
"runAfter": {
"Try_scope": ["Failed", "TimedOut"]
},
"actions": {
"Log_error": {
"type": "Http",
"inputs": {
"method": "POST",
"uri": "https://mylogging.endpoint.com/error",
"body": {
"workflowId": "@{workflow()['run']['name']}",
"errorMessage": "@{result('Try_scope')?[0]?['error']?['message']}",
"timestamp": "@utcNow()"
}
}
}
}
}
}Retry Policy
{
"Call_API": {
"type": "Http",
"inputs": { "..." : "..." },
"retryPolicy": {
"type": "exponential",
"count": 5,
"interval": "PT5S",
"minimumInterval": "PT5S",
"maximumInterval": "PT30M"
}
}
}| Retry Policy Type | Description |
|---|---|
| None | No retries — action fails immediately on error |
| Fixed | Fixed interval between retries (e.g. every 30s) |
| Exponential | Increasing intervals with jitter — recommended for HTTP calls |
| Default | 4 retries at 7.5s, 12.5s, 19.5s, 26s intervals |
09Variables & Data Operations
Variables in Logic Apps let you store and accumulate data across workflow steps — counters, arrays, intermediate results, and computed payloads. Unlike expressions which are evaluated inline, variables persist throughout the workflow run and can be updated inside loops. In production, use variables sparingly inside For Each loops (they serialize execution), and prefer Compose actions for intermediate calculations. Parse JSON is essential for turning untyped API responses into strongly-typed objects that the designer can auto-complete.
// Initialize variable
{
"Init_counter": {
"type": "InitializeVariable",
"inputs": {
"variables": [{
"name": "counter",
"type": "integer",
"value": 0
}]
}
}
}
// Increment variable
{
"Increment_counter": {
"type": "IncrementVariable",
"inputs": {
"name": "counter",
"value": 1
}
}
}
// Append to array variable
{
"Collect_results": {
"type": "AppendToArrayVariable",
"inputs": {
"name": "resultList",
"value": "@body('Process_item')"
}
}
}
// Compose — evaluate expression and store
{
"Build_payload": {
"type": "Compose",
"inputs": {
"orderId": "@triggerBody()?['id']",
"total": "@mul(triggerBody()?['qty'], triggerBody()?['price'])",
"ts": "@utcNow()"
}
}
}Parse JSON — Create Typed Schema
{
"Parse_order": {
"type": "ParseJson",
"inputs": {
"content": "@body('HTTP_get_order')",
"schema": {
"type": "object",
"properties": {
"orderId": { "type": "string" },
"amount": { "type": "number" },
"items": { "type": "array" }
}
}
}
}
}10Security & Authentication
Security in Logic Apps spans both inbound protection (who can trigger your workflow) and outbound authentication (how your workflow proves its identity to downstream services). Managed Identity is the gold standard — it eliminates stored credentials entirely and uses Azure RBAC for access control. In production, never expose Logic App trigger URLs directly, always rotate API keys stored in Key Vault, and use APIM or IP restrictions to lock down HTTP triggers. Every authentication method has trade-offs between security, complexity, and compatibility with legacy systems.
| Authentication Type | Use Case | How |
|---|---|---|
| Managed Identity | Azure services (Storage, Service Bus, SQL) | Zero-credential, RBAC role assignment |
| OAuth 2.0 | Entra ID-protected APIs | Token from Entra ID |
| API Key | Third-party APIs (SendGrid, Twilio) | Key in header or query string |
| Basic Auth | Legacy systems | Username/password in Authorization header |
| Client Certificate | High-assurance partner APIs | TLS mutual auth |
| SAS Token | Azure Storage, Service Bus (legacy) | Time-limited signature |
Managed Identity on HTTP Action
{
"Call_secure_api": {
"type": "Http",
"inputs": {
"method": "GET",
"uri": "https://myapi.azurewebsites.net/orders",
"authentication": {
"type": "ManagedServiceIdentity",
"audience": "api://my-backend-app-id"
}
}
}
}Secure HTTP Trigger with SAS
Logic App HTTP triggers generate a URL with a built-in SAS signature. Store the URL in Key Vault. You can also add IP filtering and require Entra ID tokens via APIM in front of the Logic App.
11Network Security & VNet
Network isolation is a hard requirement for enterprise workloads that process sensitive data or must comply with regulations like PCI-DSS or HIPAA. Standard Logic Apps support full VNet integration for outbound traffic and private endpoints for inbound access, ensuring your workflows never traverse the public internet. Use VNet integration when your Logic App needs to reach private databases, internal APIs, or on-premises systems through ExpressRoute or VPN gateways. For Consumption plans, the On-Premises Data Gateway provides a bridge to private networks, though with more operational overhead.
| Feature | Consumption | Standard |
|---|---|---|
| VNet Integration (outbound) | ✗ No | ✓ Yes |
| Private Endpoints (inbound) | ✗ No | ✓ Yes |
| IP Restriction on trigger | ✓ Yes | ✓ Yes |
| On-Premises Gateway | ✓ Yes | ✓ Yes |
| Managed connector VNet | ✗ No | ✓ Yes |
| Built-in connector VNet-aware | ✗ No | ✓ Yes |
VNet Integration (Standard)
# Create Standard Logic App with VNet integration
az logicapp create \
--name myLogicApp \
--resource-group myRG \
--plan myAppServicePlan \
--storage-account mystorageaccount
# Integrate with VNet subnet
az webapp vnet-integration add \
--name myLogicApp \
--resource-group myRG \
--vnet myVNet \
--subnet mySubnet12Service Bus Integration
Azure Service Bus is one of the most common integration partners for Logic Apps, enabling reliable asynchronous messaging between distributed systems. Logic Apps can trigger on queue or topic messages, process them, and explicitly complete or dead-letter based on outcome — implementing the competing consumer pattern without custom code. In production, always use peek-lock mode (not receive-and-delete) so messages return to the queue if your workflow fails mid-processing. For Standard Logic Apps, prefer the built-in Service Bus connector which is push-based, VNet-aware, and significantly faster than the managed polling connector.
| Action / Trigger | Direction | Description |
|---|---|---|
| When a message is received in a queue | Trigger | Poll queue; fire when message available |
| When a message is received in a subscription | Trigger | Poll topic subscription |
| Send message to queue | Action | Publish message to a queue |
| Send message to topic | Action | Publish to a topic (fan-out) |
| Complete the message in a queue | Action | Acknowledge successful processing |
| Abandon the message in a queue | Action | Return to queue for retry |
| Dead-letter the message in a queue | Action | Move to DLQ explicitly |
| Get messages from dead-letter queue | Action | Drain and inspect DLQ |
| Renew lock on message | Action | Extend lock for long-running workflows |
Full Pattern: Service Bus → Process → Complete/DLQ
{
"triggers": {
"When_message_arrives": {
"type": "ApiConnection",
"inputs": {
"host": { "connection": { "name": "@parameters('$connections')['servicebus']['connectionId']" } },
"method": "get",
"path": "/@{encodeURIComponent('orders')}/messages/head/peek"
},
"recurrence": { "frequency": "Second", "interval": 30 }
}
},
"actions": {
"Parse_message": {
"type": "ParseJson",
"inputs": {
"content": "@base64ToString(triggerBody()?['ContentData'])",
"schema": { "..." : "..." }
}
},
"Process_order": {
"type": "Http",
"inputs": { "method": "POST", "uri": "https://api.mybackend.com/orders", "body": "@body('Parse_message')" },
"runAfter": { "Parse_message": ["Succeeded"] }
},
"Complete_message": {
"type": "ApiConnection",
"inputs": {
"host": { "connection": { "name": "@parameters('$connections')['servicebus']['connectionId']" } },
"method": "delete",
"path": "/@{encodeURIComponent('orders')}/messages/complete",
"queries": { "lockToken": "@triggerBody()?['LockToken']" }
},
"runAfter": { "Process_order": ["Succeeded"] }
},
"Deadletter_on_failure": {
"type": "ApiConnection",
"inputs": {
"host": { "connection": { "name": "@parameters('$connections')['servicebus']['connectionId']" } },
"method": "post",
"path": "/@{encodeURIComponent('orders')}/messages/deadletter",
"queries": {
"lockToken": "@triggerBody()?['LockToken']",
"deadLetterReason": "ProcessingFailed",
"deadLetterErrorDescription": "@{body('Process_order')}"
}
},
"runAfter": { "Process_order": ["Failed", "TimedOut"] }
}
}
}13Storage Integration
Azure Storage is deeply integrated with Logic Apps, covering Blob Storage for file processing, Queue Storage for lightweight messaging, and Table Storage for simple key-value data. Common patterns include triggering workflows when files land in blob containers, processing CSV/JSON uploads, and archiving workflow outputs. In production, be aware that the blob trigger uses polling with a minimum 1-minute delay — for near-real-time file processing, pair Event Grid with an HTTP trigger instead. Use Managed Identity authentication to avoid storing connection strings in your workflow configuration.
| Action / Trigger | Description |
|---|---|
| When a blob is added or modified (V2) | Trigger on blob change — polling |
| Create blob (V2) | Upload content as a new blob |
| Get blob content (V2) | Download blob body into workflow variable |
| Get blob metadata | Read blob properties, tags, content type |
| List blobs (V2) | Enumerate container contents with prefix filter |
| Delete blob (V2) | Remove a blob |
| Copy blob | Server-side copy between containers or accounts |
| Extract archive to folder | Decompress zip archive blob |
| Put a message on a queue | Enqueue message to Storage Queue |
| Get messages from a queue | Dequeue messages from Storage Queue |
| Insert entity (Table) | Add a new row to Azure Table Storage |
| Get entity (Table) | Retrieve a row by PartitionKey + RowKey |
| Query entities (Table) | OData filter query across table rows |
14Function Apps Integration
Logic Apps can call Azure Functions as actions — offloading complex code logic, transformations, or computations that would be awkward in expressions.
{
"Call_function": {
"type": "Function",
"inputs": {
"function": {
"id": "/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.Web/sites/<funcApp>/functions/<funcName>"
},
"method": "POST",
"headers": { "Content-Type": "application/json" },
"body": {
"orderId": "@triggerBody()?['orderId']",
"items": "@body('Parse_order')?['items']"
}
}
}
}15APIM Integration
Two integration directions: APIM can call Logic Apps as a backend, and Logic Apps can call APIM-managed APIs as actions.
APIM → Logic App (Expose LA as REST API)
- 1Import Logic App into APIMAPIM → APIs → Add API → Logic App. Auto-discovers the HTTP trigger schema.
- 2Set backend authenticationAPIM uses Managed Identity to call the Logic App trigger URL — no SAS in URL.
- 3Add inbound policiesRate limit, JWT validation, CORS — all applied before Logic App receives the call.
- 4Hide Logic App URLClients only see the APIM gateway URL — Logic App URL stays internal.
Logic App → APIM-managed API
{
"Call_orders_api_via_APIM": {
"type": "Http",
"inputs": {
"method": "POST",
"uri": "https://myAPIM.azure-api.net/orders",
"headers": {
"Ocp-Apim-Subscription-Key": "@parameters('apimSubscriptionKey')",
"Content-Type": "application/json"
},
"body": "@triggerBody()"
}
}
}16Event Grid Integration
Event Grid delivers events to Logic Apps via a push webhook — no polling, near-instant delivery. Subscribe to events from Storage, Service Bus, Resource Manager, custom topics, and 60+ Azure services.
// Logic App trigger for Event Grid events
{
"triggers": {
"When_Event_Grid_event_occurs": {
"type": "ApiConnectionWebhook",
"inputs": {
"host": { "connection": { "name": "@parameters('$connections')['azureeventgrid']['connectionId']" } },
"path": "/subscriptions/@{encodeURIComponent('<sub-id>')}/providers/@{encodeURIComponent('Microsoft.Storage.StorageAccounts')}/resource/eventSubscriptions",
"body": {
"properties": {
"destination": { "endpointType": "webhook", "properties": { "endpointUrl": "@{listCallbackUrl()}" } },
"filter": { "includedEventTypes": ["Microsoft.Storage.BlobCreated"] }
}
}
}
}
}
}17SQL & Cosmos DB Integration
Database integration is central to most enterprise workflows — whether you are reading order data, writing audit logs, or syncing records between systems. Logic Apps provides native connectors for both Azure SQL and Cosmos DB with triggers that detect new or modified data automatically. For SQL, prefer stored procedures over inline queries for complex operations to keep your workflow definitions clean and maintainable. For Cosmos DB, the change feed trigger is particularly powerful — it fires on every document create or update, enabling real-time event-driven architectures without polling overhead.
SQL Server Actions
| Action | Description |
|---|---|
| Execute a SQL query | Run any SELECT/INSERT/UPDATE/DELETE statement |
| Get rows | Return rows from a table with OData filter |
| Insert row | Add a new row to a table |
| Update row | Modify an existing row by primary key |
| Delete row | Remove a row by primary key |
| Execute stored procedure | Call a stored procedure with parameters |
| When an item is created (trigger) | Poll for new rows inserted since last run |
| When an item is modified (trigger) | Poll for updated rows |
Cosmos DB Actions
| Action | Description |
|---|---|
| Create or update document | Upsert a JSON document by ID |
| Get document | Retrieve a document by ID and partition key |
| Query documents | Execute a SQL API query |
| Delete document | Remove a document by ID |
| When a document is created or modified (trigger) | Change feed trigger — new/modified docs |
18Other Azure Services
Beyond the core integration services, Logic Apps connects to the broader Microsoft ecosystem including Office 365, Teams, SharePoint, Key Vault, Azure Monitor, and AI services. These connectors turn Logic Apps into a universal automation hub — processing emails, posting notifications, managing secrets, and even calling AI models as workflow steps. In production, combine multiple connectors to build end-to-end business processes: for example, an email attachment triggers document AI extraction, results go to SQL, and a Teams notification confirms completion. Always use Key Vault references for secrets rather than hardcoding values in parameters.
19Monitoring & Diagnostics
Monitoring is non-negotiable for production Logic Apps — you need visibility into run success rates, latency, and failure root causes. Azure provides built-in run history with full input/output capture for every action, plus integration with Azure Monitor metrics and Log Analytics for alerting and KQL-based querying. Set up alerts on failed runs immediately after deployment — silent failures are the most dangerous kind. For high-volume workflows, enable diagnostic settings to stream logs to Log Analytics where you can build dashboards and correlate failures across multiple workflows.
Run History
Every workflow run is stored with full inputs, outputs, duration, and status of every action. Available in Azure Portal → Logic App → Run History. Retention: 90 days (Consumption), configurable (Standard).
| Metric | Description | Alert Condition |
|---|---|---|
| Runs Started | Total workflow executions | Spike or drop detection |
| Runs Succeeded | Successful completions | Low vs Runs Started = failures |
| Runs Failed | Failed executions | > 0 — always alert |
| Run Latency | Average workflow duration | > expected threshold |
| Actions Started / Succeeded / Failed | Per-action granularity | Failed > 0 |
| Billing Executions | Billable action count | Cost monitoring |
KQL — Failed Runs in Last Hour
AzureDiagnostics
| where ResourceType == "WORKFLOWS/RUNS"
| where status_s == "Failed"
| where TimeGenerated > ago(1h)
| project TimeGenerated, resource_workflowName_s,
startTime_t, endTime_t, error_code_s, error_message_s
| order by TimeGenerated desc20CI/CD & DevOps
Treating Logic Apps as code is essential for team collaboration, environment promotion, and rollback safety. Standard Logic Apps use a file-based structure that fits naturally into Git repositories and CI/CD pipelines, while Consumption Logic Apps rely on ARM/Bicep templates for infrastructure-as-code deployment. Always parameterize environment-specific values (connection strings, URLs, keys) so the same workflow definition deploys cleanly across dev, staging, and production. Use GitHub Actions or Azure DevOps pipelines to automate deployment on every merge to main, with approval gates for production.
Standard Logic App — File Structure
myLogicApp/
├── host.json ← Runtime configuration
├── local.settings.json ← Local dev settings
├── connections.json ← Connector connections
├── parameters.json ← Parameter values per environment
└── Workflows/
├── OrderProcessing/
│ └── workflow.json ← Workflow definition
└── DLQHandler/
└── workflow.jsonGitHub Actions — Deploy Standard Logic App
name: Deploy Logic App
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Login to Azure
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Deploy Logic App workflows
uses: Azure/logicapps-deploy@v2
with:
la-name: myLogicApp
resource-group: myRG
artifacts-location: ./myLogicAppConsumption — Export & Deploy via ARM
# Export existing Logic App as ARM template
az logic workflow export \
--resource-group myRG \
--name myLogicApp \
--sku Free > workflow-arm.json
# Deploy ARM template
az deployment group create \
--resource-group myRG \
--template-file workflow-arm.json \
--parameters @parameters.prod.json21Architecture Patterns
Logic Apps shines brightest when used as the orchestration layer in well-known enterprise integration patterns. These patterns solve recurring problems — event fan-out, document pipelines, human-in-the-loop approvals, B2B message exchange, and scheduled data synchronization. In production, combine multiple patterns: an event-driven trigger feeds into a document processing pipeline that ends with a notification. Start with a proven pattern rather than inventing custom architectures, and you will get to production faster with fewer edge cases to handle.
22Logic Apps vs Functions vs APIM
One of the most common architecture questions is when to use Logic Apps versus Azure Functions versus API Management — and the answer is usually "all three together." Each service excels at a different layer: APIM handles API security and traffic management, Logic Apps orchestrates multi-step workflows and SaaS integrations, and Functions executes custom business logic and computations. Understanding their strengths prevents you from forcing one tool to do another's job. Use this comparison to assign the right responsibility to the right service in your architecture.
| Dimension | Logic Apps | Azure Functions | APIM |
|---|---|---|---|
| Primary purpose | Workflow orchestration & integration | Event-driven compute & code execution | API gateway & management |
| Programming model | Visual / declarative JSON | Code (C#, JS, Python, Java, PS) | XML policy + config |
| Connectors | 500+ managed connectors | Bindings (fewer, code-driven) | Via send-request policy |
| State management | Built-in stateful runs | Durable Functions only | Stateless per request |
| Long-running | Days/weeks (stateful) | Only with Durable Functions | No (sync only) |
| Best for | SaaS integrations, approvals, ETL | Custom logic, APIs, event processing | API security, routing, rate limiting |
| Cold start | Minimal | Can occur (Consumption plan) | None (always warm) |
| Local dev | VS Code (Standard) | Full local emulation | Limited |
| Pricing unit | Per action / per vCPU | Per execution + GB-s | Per call / per unit/hr |
23Pricing Overview
Logic Apps pricing varies dramatically between Consumption (pay-per-action) and Standard (fixed compute) plans, and choosing wrong can mean overpaying by 10x or more. Consumption is cheapest for workflows that run a few times per day, but costs escalate quickly with high-volume loops and enterprise connectors. Standard provides predictable monthly costs and is more economical once you exceed roughly 100,000 actions per month. Always estimate your expected action count, connector types, and run frequency before selecting a plan — and monitor billing metrics after deployment to catch surprises early.
Consumption Plan
| Resource | Price |
|---|---|
| Actions (first 4,000/month) | Free |
| Actions | ~$0.000025 per execution |
| Standard connector calls | ~$0.000125 per call |
| Enterprise connector calls | ~$0.001 per call |
| B2B messages (AS2/X12) | ~$0.10–$1.00 per 1,000 messages |
Standard Plan
| SKU | vCPU | Memory | Price (approx) |
|---|---|---|---|
| WS1 | 1 | 3.5 GB | ~$190/month |
| WS2 | 2 | 7 GB | ~$380/month |
| WS3 | 4 | 14 GB | ~$760/month |
24Quick Reference Cheat Sheet
This cheat sheet consolidates the most frequently used expressions, Service Bus message properties, and platform limits into a single quick-reference section. Bookmark this for daily development — you will reach for these expressions constantly when building and debugging workflows. The limits table helps you design within platform constraints from the start, avoiding surprises when workflows hit production scale. Print it, pin it, or keep it in a browser tab — these are the values every Logic Apps developer needs at their fingertips.
@triggerBody()?['field'] — Trigger payload field
@body('Action_Name')?['field'] — Action output field
@outputs('Action')?['statusCode'] — HTTP status code
@variables('myVar') — Variable value
@parameters('myParam') — Parameter value
@utcNow() — Current UTC timestamp
@utcNow('yyyy-MM-dd') — Formatted date
@guid() — New GUID
@workflow()['run']['name'] — Current run ID
@result('Scope_Name') — Scope result array
@concat('a', 'b', variables('c')) — String concatenation
@coalesce(val1, val2, 'default') — First non-null value@triggerBody()?['ContentData'] — Base64 message body
@base64ToString(triggerBody()?['ContentData']) — Decoded body
@triggerBody()?['LockToken'] — Lock token for complete/abandon
@triggerBody()?['MessageId'] — Message ID
@triggerBody()?['SessionId'] — Session ID
@triggerBody()?['CorrelationId'] — Correlation ID
@triggerBody()?['Properties']?['key'] — Custom property| Limit | Consumption | Standard |
|---|---|---|
| Max workflow duration | 90 days | Unlimited (stateful) |
| Max action outputs retained | 7 days | Configurable |
| Max request body size | 100 MB | 100 MB |
| Max For Each concurrency | 20 parallel | 20 parallel |
| Max Until iterations | 5,000 | 5,000 |
| Max connections per workflow | Unlimited | Unlimited |
| Workflows per app | 1 | Unlimited |
| Run history retention | 90 days | Configurable |