← Back to Guides

Standard Logic Apps Development

AdvancedAzure Logic Apps2026-03-14

Why Standard Logic Apps?

Standard Logic Apps run on a single-tenant runtime based on the Azure Functions extensibility model. This provides significant advantages for enterprise development.

Microsoft Reference: Single-tenant Logic Apps overview

Key Advantages Over Consumption

Consumption

Multi-tenant — serverless, fully managed
Multiple workflows1 per resource
Local developmentNot supported
DebuggingPortal only
VNET integrationISE (deprecated)
DeploymentARM/Bicep
Stateless workflowsNot supported
Custom connectorsNot supported
Storage controlMicrosoft-managed
Version controlExport/import

Standard

Single-tenant — full development experience
Multiple workflowsUnlimited per app
Local developmentFull VS Code support
DebuggingVS Code breakpoints
VNET integrationNative VNET integration
DeploymentZIP deploy, containers
Stateless workflowsSupported (high performance)
Custom connectorsExtensibility framework
Storage controlYour storage account
Version controlFull Git integration

Development Environment Setup

Prerequisites

  1. Visual Studio CodeDownload
  2. Azure Logic Apps (Standard) extension — Install from VS Code marketplace
  3. Azure Functions Core Tools v4npm install -g azure-functions-core-tools@4
  4. Azurite — Local Azure Storage emulator (included in VS Code Azure extensions)
  5. .NET 6.0 SDK — Required for the Logic Apps runtime
  6. Azure CLI — For deployment and resource management

Microsoft Reference: Set up VS Code for Logic Apps

Project Initialisation

# Create project via VS Code
# 1. Open Command Palette (Ctrl+Shift+P)
# 2. Select "Azure Logic Apps: Create New Project"
# 3. Choose project folder
# 4. Select "Stateful Workflow" or "Stateless Workflow"

Project Structure

logic-app-project/
├── .vscode/
│   ├── extensions.json      # Recommended extensions
│   ├── launch.json          # Debug configuration
│   ├── settings.json        # Workspace settings
│   └── tasks.json           # Build tasks
├── Artifacts/
│   ├── Maps/                # XSLT maps for data transformation
│   └── Schemas/             # XML/JSON schemas for validation
├── lib/
│   └── custom/              # Custom connector assemblies (.NET)
├── workflow1/
│   └── workflow.json        # Workflow definition
├── workflow2/
│   └── workflow.json        # Another workflow definition
├── connections.json          # Connector configurations
├── host.json                 # Runtime configuration
├── local.settings.json       # Local environment variables
└── parameters.json           # Workflow parameters

host.json

Configure runtime behaviour:

{
  "version": "2.0",
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle.Workflows",
    "version": "[1.*, 2.0.0)"
  },
  "extensions": {
    "workflow": {
      "settings": {
        "Runtime.FlowRunRetryableActionJobCallback.MaximumRetries": "3",
        "Runtime.Backend.FlowDefaultTimeout": "00:05:00"
      }
    }
  },
  "logging": {
    "logLevel": {
      "default": "Information",
      "Host.Triggers.Workflow": "Debug"
    },
    "applicationInsights": {
      "samplingSettings": {
        "isEnabled": true,
        "maxTelemetryItemsPerSecond": 20
      }
    }
  }
}

connections.json

Define connections for managed connectors:

{
  "managedApiConnections": {
    "office365": {
      "api": {
        "id": "/subscriptions/{sub}/providers/Microsoft.Web/locations/{location}/managedApis/office365"
      },
      "connection": {
        "id": "/subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/connections/office365"
      },
      "connectionRuntimeUrl": "https://{connection-runtime-url}",
      "authentication": {
        "type": "ManagedServiceIdentity"
      }
    }
  },
  "serviceProviderConnections": {
    "serviceBus": {
      "parameterValues": {
        "connectionString": "@appsetting('ServiceBus_ConnectionString')"
      },
      "serviceProvider": {
        "id": "/serviceProviders/serviceBus"
      },
      "displayName": "Service Bus Connection"
    }
  }
}

local.settings.json

Local development environment variables:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "node",
    "WORKFLOWS_SUBSCRIPTION_ID": "{subscription-id}",
    "WORKFLOWS_RESOURCE_GROUP_NAME": "{resource-group}",
    "WORKFLOWS_LOCATION_NAME": "uksouth",
    "WORKFLOWS_MANAGEMENT_BASE_URI": "https://management.azure.com/",
    "ServiceBus_ConnectionString": "Endpoint=sb://...",
    "SQL_ConnectionString": "Server=...",
    "AppInsights_InstrumentationKey": "{key}"
  }
}

Stateful vs Stateless Workflows

Stateful Workflows

  • Run history persisted in storage account
  • Input/output retention for debugging
  • Durable execution — survives host restarts
  • Higher latency due to storage operations
  • Best for: Long-running processes, workflows needing audit trails

Stateless Workflows

  • No run history persisted (in-memory only)
  • Lower latency — no storage overhead
  • Best for: High-throughput, request-response scenarios
  • Cannot use: Chunking, managed connectors (built-in only), durable timers

Choosing Between Them

Scenario Stateful Stateless
API orchestration (request-response) Preferred
Order processing with audit trail Preferred
Real-time data transformation Preferred
Long-running approval workflows Preferred
High-throughput event processing Preferred
B2B/EDI message processing Preferred

Parameters and App Settings

Workflow Parameters

Define reusable values across workflows in parameters.json:

{
  "apiBaseUrl": {
    "type": "String",
    "value": "https://api.example.com/v1"
  },
  "maxRetries": {
    "type": "Int",
    "value": 3
  },
  "notificationEmail": {
    "type": "String",
    "value": "ops@example.com"
  },
  "enableDetailedLogging": {
    "type": "Bool",
    "value": true
  }
}

Reference in workflows:

{
  "HTTP_Call": {
    "type": "Http",
    "inputs": {
      "method": "GET",
      "uri": "@{parameters('apiBaseUrl')}/orders"
    }
  }
}

App Settings vs Parameters

Feature App Settings Parameters
Scope Application-wide Per workflow
Change Restart required Workflow reload
Secrets Key Vault references Not recommended
CI/CD Slot settings, ARM parameters.json per environment

Key Vault References in App Settings

@Microsoft.KeyVault(SecretUri=https://kv-logicapp-prod.vault.azure.net/secrets/ApiKey/)
@Microsoft.KeyVault(VaultName=kv-logicapp-prod;SecretName=ApiKey)

Microsoft Reference: Key Vault references

Local Debugging

Start Local Runtime

# Start Azurite storage emulator
azurite --silent --location .azurite --debug .azurite/debug.log

# Start Logic App runtime
func host start

VS Code Debug Configuration

.vscode/launch.json:

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Attach to Logic App",
      "type": "coreclr",
      "request": "attach",
      "processId": "${command:azureLogicAppsStandard.pickProcess}"
    }
  ]
}

Testing Workflows Locally

  1. Start the runtime with func host start
  2. For HTTP-triggered workflows, the local URL is displayed: http://localhost:7071/api/workflow-name/triggers/manual/invoke?api-version=2020-05-01-preview
  3. Send requests using curl, Postman, or the REST Client VS Code extension
  4. View run history in VS Code or the Azure portal (when connected)

Local Testing with Mock Data

Create test data files for trigger payloads:

// test-data/order-payload.json
{
  "orderId": "ORD-001",
  "customerId": "CUST-100",
  "items": [
    { "productId": "PROD-A", "quantity": 2, "price": 29.99 }
  ],
  "total": 59.98
}
curl -X POST http://localhost:7071/api/process-order/triggers/manual/invoke \
  -H "Content-Type: application/json" \
  -d @test-data/order-payload.json \
  "?api-version=2020-05-01-preview"

Deployment

ZIP Deployment

# Build the project
dotnet publish -c Release -o ./publish

# Create ZIP package
cd publish && zip -r ../logic-app.zip . && cd ..

# Deploy to Azure
az logicapp deployment source config-zip \
  --resource-group rg-integration-prod \
  --name la-standard-prod \
  --src logic-app.zip

Bicep Deployment

param location string = resourceGroup().location
param environment string

resource appServicePlan 'Microsoft.Web/serverfarms@2022-09-01' = {
  name: 'asp-logicapp-${environment}'
  location: location
  sku: {
    tier: 'WorkflowStandard'
    name: environment == 'prod' ? 'WS2' : 'WS1'
  }
  properties: {
    targetWorkerCount: environment == 'prod' ? 3 : 1
    maximumElasticWorkerCount: environment == 'prod' ? 20 : 5
    zoneRedundant: environment == 'prod'
  }
}

resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
  name: 'stlogicapp${environment}'
  location: location
  sku: { name: 'Standard_ZRS' }
  kind: 'StorageV2'
  properties: {
    minimumTlsVersion: 'TLS1_2'
    supportsHttpsTrafficOnly: true
  }
}

resource logicApp 'Microsoft.Web/sites@2022-09-01' = {
  name: 'la-standard-${environment}'
  location: location
  kind: 'functionapp,workflowapp'
  identity: {
    type: 'SystemAssigned'
  }
  properties: {
    serverFarmId: appServicePlan.id
    httpsOnly: true
    virtualNetworkSubnetId: subnetId
    siteConfig: {
      netFrameworkVersion: 'v6.0'
      ftpsState: 'Disabled'
      appSettings: [
        {
          name: 'AzureWebJobsStorage'
          value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};EndpointSuffix=core.windows.net;AccountKey=${storageAccount.listKeys().keys[0].value}'
        }
        {
          name: 'FUNCTIONS_EXTENSION_VERSION'
          value: '~4'
        }
        {
          name: 'FUNCTIONS_WORKER_RUNTIME'
          value: 'node'
        }
        {
          name: 'WEBSITE_NODE_DEFAULT_VERSION'
          value: '~18'
        }
        {
          name: 'APP_KIND'
          value: 'workflowApp'
        }
        {
          name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
          value: appInsights.properties.ConnectionString
        }
      ]
    }
  }
}

Azure DevOps Pipeline for Standard Logic Apps

trigger:
  branches:
    include:
      - main
  paths:
    include:
      - src/logic-apps/*

variables:
  - group: logic-app-settings
  - name: workingDirectory
    value: 'src/logic-apps'

stages:
  - stage: Build
    jobs:
      - job: BuildLogicApp
        pool:
          vmImage: 'ubuntu-latest'
        steps:
          - task: UseDotNet@2
            inputs:
              packageType: 'sdk'
              version: '6.x'

          - script: |
              dotnet publish $(workingDirectory) -c Release -o $(Build.ArtifactStagingDirectory)/logic-app
            displayName: 'Build Logic App'

          - publish: $(Build.ArtifactStagingDirectory)/logic-app
            artifact: logic-app

  - stage: DeployDev
    dependsOn: Build
    jobs:
      - deployment: DeployToDev
        environment: 'logic-app-dev'
        strategy:
          runOnce:
            deploy:
              steps:
                - download: current
                  artifact: logic-app

                - task: AzureFunctionApp@2
                  displayName: 'Deploy Logic App'
                  inputs:
                    azureSubscription: 'dev-service-connection'
                    appType: 'functionApp'
                    appName: 'la-standard-dev'
                    package: '$(Pipeline.Workspace)/logic-app/**/*.zip'
                    deploymentMethod: 'zipDeploy'

  - stage: DeployProd
    dependsOn: DeployDev
    condition: succeeded()
    jobs:
      - deployment: DeployToProd
        environment: 'logic-app-prod'
        strategy:
          runOnce:
            deploy:
              steps:
                - download: current
                  artifact: logic-app

                - task: AzureFunctionApp@2
                  displayName: 'Deploy Logic App'
                  inputs:
                    azureSubscription: 'prod-service-connection'
                    appType: 'functionApp'
                    appName: 'la-standard-prod'
                    package: '$(Pipeline.Workspace)/logic-app/**/*.zip'
                    deploymentMethod: 'zipDeploy'

VNET Integration

Configure VNET Integration

resource logicApp 'Microsoft.Web/sites@2022-09-01' = {
  // ...
  properties: {
    virtualNetworkSubnetId: integrationSubnet.id
    vnetRouteAllEnabled: true  // Route all traffic through VNET
    siteConfig: {
      vnetPrivatePortsCount: 2  // Private endpoints for outbound
    }
  }
}

// Private endpoint for inbound traffic
resource privateEndpoint 'Microsoft.Network/privateEndpoints@2023-05-01' = {
  name: 'pe-logicapp-${environment}'
  location: location
  properties: {
    subnet: {
      id: privateEndpointSubnet.id
    }
    privateLinkServiceConnections: [
      {
        name: 'logicapp-connection'
        properties: {
          privateLinkServiceId: logicApp.id
          groupIds: ['sites']
        }
      }
    ]
  }
}

Network Architecture

Internet → (blocked by private endpoint)
APIM (VNET) → Private Endpoint → Logic App (VNET integrated)
Logic App → VNET Integration → Service Bus / SQL / Key Vault (via service endpoints or private endpoints)

Scaling and Performance

App Service Plan Sizing

Plan vCPU Memory Max Instances Use Case
WS1 1 3.5 GB 20 Development, low-volume
WS2 2 7 GB 20 Production, medium-volume
WS3 4 14 GB 20 High-volume, complex workflows

Autoscaling Rules

resource autoscale 'Microsoft.Insights/autoscalesettings@2022-10-01' = {
  name: 'autoscale-logicapp'
  location: location
  properties: {
    targetResourceUri: appServicePlan.id
    enabled: true
    profiles: [
      {
        name: 'Default'
        capacity: {
          minimum: '2'
          maximum: '10'
          default: '2'
        }
        rules: [
          {
            metricTrigger: {
              metricName: 'CpuPercentage'
              metricResourceUri: appServicePlan.id
              timeGrain: 'PT1M'
              statistic: 'Average'
              timeWindow: 'PT5M'
              operator: 'GreaterThan'
              threshold: 70
            }
            scaleAction: {
              direction: 'Increase'
              type: 'ChangeCount'
              value: '1'
              cooldown: 'PT5M'
            }
          }
          {
            metricTrigger: {
              metricName: 'CpuPercentage'
              metricResourceUri: appServicePlan.id
              timeGrain: 'PT1M'
              statistic: 'Average'
              timeWindow: 'PT10M'
              operator: 'LessThan'
              threshold: 30
            }
            scaleAction: {
              direction: 'Decrease'
              type: 'ChangeCount'
              value: '1'
              cooldown: 'PT10M'
            }
          }
        ]
      }
    ]
  }
}

Best Practices

  1. Use stateless workflows for high-throughput request-response scenarios
  2. Store secrets in Key Vault and reference via app settings
  3. Enable Application Insights for monitoring and diagnostics
  4. Use VNET integration for secure communication with backend services
  5. Implement CI/CD with Azure DevOps or GitHub Actions for consistent deployments
  6. Use parameters.json for environment-specific configuration
  7. Enable zone redundancy for production workloads
  8. Set up autoscaling based on CPU and memory metrics
  9. Use deployment slots for zero-downtime deployments
  10. Keep workflows focused — prefer multiple small workflows over one large workflow

Official Microsoft Resources