On This Page
1Overview2Architecture Overview
3Azure Service Topology4Implementation Guide
5Decision Criteria6Cost Model
7Anti-Patterns to Avoid8References

Overview

The serverless pattern on Azure inverts the traditional deployment model. Instead of running servers continuously, you deploy functions or containers and Azure runs them only when triggered. Functions on the Consumption plan cost nothing when idle and scale horizontally for concurrent requests. The Premium plan adds VNet integration, no cold starts, and execution up to 60 minutes — necessary for user-facing APIs where a 1–4 second cold start on .NET or Node.js (worse on Java) is unacceptable.

The key architectural decision in Azure serverless is choosing between Azure Functions for single-operation event handlers, Azure Container Apps for workloads that exceed Function runtime constraints (execution time, memory, custom runtimes), and Durable Functions for stateful workflows requiring orchestrator fan-out, fan-in, and async HTTP polling patterns.

Architecture Overview

%%{init:{'theme':'base','themeVariables':{'fontSize':'14px','fontFamily':'Inter, system-ui, sans-serif','primaryColor':'#DBEAFE','primaryTextColor':'#1e3a5f','primaryBorderColor':'#2563EB','lineColor':'#374151','clusterBkg':'#F9FAFB','clusterBorder':'#D1D5DB','edgeLabelBackground':'#FFFFFF'},'flowchart':{'curve':'orthogonal','padding':30,'nodeSpacing':65,'rankSpacing':75,'useMaxWidth':true}}}%% flowchart TD START([Client Request or Event Trigger]) START --> TRIGGER{Trigger Type} TRIGGER -->|HTTP request| APIM[API Management\nRate limit, auth, caching\nRouting and versioning] TRIGGER -->|Schedule| TIMER[Timer Trigger\nCron expression\nRetry with poison queue] TRIGGER -->|Queue message| SB_T[Service Bus Queue\nBatch processing\nDead letter queue] TRIGGER -->|Multi-step workflow| DURABLE[Durable Functions\nOrchestrator pattern\nFan-out fan-in, async HTTP] APIM --> FUNC[Azure Functions\nStateless execution\nConsumption or Premium plan] TIMER --> FUNC SB_T --> FUNC DURABLE --> FUNC FUNC --> DECISION{Response Type} DECISION -->|Synchronous| RESP[Return response\nvia API Management] DECISION -->|Async continuation| NEXT[Publish message\nto Service Bus] DECISION -->|Workflow step| DURABLE RESP --> END([Response Returned to Client]) NEXT --> END style START fill:#4f8ef7,color:#fff style END fill:#10b981,color:#fff style DURABLE fill:#e0f2fe style FUNC fill:#e0f2fe

Azure Service Topology

%%{init:{'theme':'base','themeVariables':{'fontSize':'14px','fontFamily':'Inter, system-ui, sans-serif','primaryColor':'#DBEAFE','primaryTextColor':'#1e3a5f','primaryBorderColor':'#2563EB','lineColor':'#374151','clusterBkg':'#F9FAFB','clusterBorder':'#D1D5DB','edgeLabelBackground':'#FFFFFF'},'flowchart':{'curve':'orthogonal','padding':30,'nodeSpacing':65,'rankSpacing':75,'useMaxWidth':true}}}%% flowchart TD CLIENT[Client Application\nWeb, Mobile, or Service] subgraph GATEWAY["Azure API Management — front door for all backends"] AUTH[Policy: JWT Validation\nOAuth 2.0 / AAD\nPer-client rate limiting] CACHE[Response Caching\nBuilt-in Redis cache\nVary by header or query] ROUTE[Backend Routing\nVersioned APIs\nCanary release support] end subgraph FUNCTIONS["Azure Functions — Premium Plan EP1"] FN_API[HTTP-Triggered Functions\ndotnet-isolated runtime\nManaged Identity auth] FN_DURABLE[Durable Functions\nOrchestrator + Activity\nFan-out fan-in pattern] FN_SB[Service Bus Triggered\nBatch dequeue\nPoison message handling] end subgraph CONTAINERS["Azure Container Apps"] CA_APP[Container App\nKEDA autoscaling\nminReplicas 1, maxReplicas 50] CA_ENV[Managed Environment\nLog Analytics workspace\nZone redundant] end subgraph BACKEND["Backend Services"] SB_BE[Service Bus\nQueues and topics\nAt-least-once delivery] KV[Key Vault\nSecrets and certificates\nManaged Identity access] COSMOS[Cosmos DB\nNoSQL, multi-region\nServerless or autoscale RU] ACR[Container Registry\nPrivate image store\nGeo-replication] end subgraph OBS["Observability"] AI[Application Insights\nDistributed tracing\nLive metrics stream] LA[Log Analytics\nKQL queries\nAlerting and dashboards] end CLIENT --> AUTH --> CACHE --> ROUTE ROUTE --> FN_API & CA_APP FN_API --> FN_DURABLE & FN_SB CA_APP --> CA_ENV FN_API --> SB_BE & KV & COSMOS CA_APP --> SB_BE & KV & COSMOS & ACR FN_API --> AI CA_APP --> AI AI --> LA style CLIENT fill:#4f8ef7,color:#fff style FN_DURABLE fill:#e0f2fe style CA_ENV fill:#e0f2fe style KV fill:#fef3c7

Implementation Guide

Bicep — Function App on Premium Plan

The Premium plan (EP1/ElasticPremium) eliminates cold starts, enables VNet integration, and allows execution up to 60 minutes. Managed Identity replaces all connection strings.

resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' existing = {
  name: storageAccountName
}

resource hostingPlan 'Microsoft.Web/serverfarms@2022-09-01' = {
  name: '${prefix}-plan'
  location: location
  sku: {
    name: 'EP1'
    tier: 'ElasticPremium'
  }
  kind: 'elastic'
  properties: {
    maximumElasticWorkerCount: 20
    reserved: false
  }
}

resource functionApp 'Microsoft.Web/sites@2022-09-01' = {
  name: '${prefix}-func'
  location: location
  kind: 'functionapp'
  identity: {
    type: 'SystemAssigned'
  }
  properties: {
    serverFarmId: hostingPlan.id
    httpsOnly: true
    siteConfig: {
      minTlsVersion: '1.2'
      netFrameworkVersion: 'v8.0'
      appSettings: [
        {
          name: 'FUNCTIONS_EXTENSION_VERSION'
          value: '~4'
        }
        {
          name: 'FUNCTIONS_WORKER_RUNTIME'
          value: 'dotnet-isolated'
        }
        {
          // Managed Identity auth — no storage key in config
          name: 'AzureWebJobsStorage__accountName'
          value: storageAccount.name
        }
        {
          // Managed Identity auth — no Service Bus connection string
          name: 'ServiceBusConnection__fullyQualifiedNamespace'
          value: '${serviceBusNamespaceName}.servicebus.windows.net'
        }
        {
          name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
          value: appInsights.properties.ConnectionString
        }
      ]
    }
  }
}

// Grant Function App identity access to Service Bus
resource sbRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(serviceBusNamespace.id, functionApp.id, sbDataReceiverRoleId)
  scope: serviceBusNamespace
  properties: {
    roleDefinitionId: subscriptionResourceId(
      'Microsoft.Authorization/roleDefinitions', sbDataReceiverRoleId)
    principalId: functionApp.identity.principalId
    principalType: 'ServicePrincipal'
  }
}

Terraform equivalent: Use azurerm_linux_function_app with identity { type = "SystemAssigned" }. Set app_settings with AzureWebJobsStorage__accountName and ServiceBusConnection__fullyQualifiedNamespace. Assign azurerm_role_assignment for Azure Service Bus Data Receiver scoped to the namespace.

Bicep — Container Apps Environment and App

Container Apps provide managed Kubernetes with KEDA scaling. Internal ingress keeps traffic off the public internet; API Management is the sole external entry point.

resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = {
  name: logAnalyticsWorkspaceName
}

resource managedEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = {
  name: '${prefix}-cae'
  location: location
  properties: {
    appLogsConfiguration: {
      destination: 'log-analytics'
      logAnalyticsConfiguration: {
        customerId: logAnalytics.properties.customerId
        sharedKey: logAnalytics.listKeys().primarySharedKey
      }
    }
    zoneRedundant: true
  }
}

resource containerApp 'Microsoft.App/containerApps@2023-05-01' = {
  name: '${prefix}-ca'
  location: location
  identity: {
    type: 'SystemAssigned'
  }
  properties: {
    managedEnvironmentId: managedEnvironment.id
    configuration: {
      ingress: {
        external: false          // internal only — APIM is front door
        targetPort: 8080
        transport: 'http2'
        allowInsecure: false
      }
      registries: [
        {
          server: containerRegistry.properties.loginServer
          identity: 'system'   // Managed Identity pull from ACR
        }
      ]
    }
    template: {
      containers: [
        {
          name: 'api'
          image: '${containerRegistry.properties.loginServer}/api:${imageTag}'
          resources: {
            cpu: json('0.5')
            memory: '1Gi'
          }
          env: [
            {
              name: 'COSMOS_ENDPOINT'
              value: cosmosAccount.properties.documentEndpoint
            }
          ]
        }
      ]
      scale: {
        minReplicas: 1           // avoid cold start for user-facing traffic
        maxReplicas: 50
        rules: [
          {
            name: 'http-scaling'
            http: {
              metadata: {
                concurrentRequests: '50'
              }
            }
          }
        ]
      }
    }
  }
}

Terraform equivalent: Use azurerm_container_app_environment with log_analytics_workspace_id and zone_redundancy_enabled = true. Use azurerm_container_app with identity { type = "SystemAssigned" } and ingress { external_enabled = false, transport = "http2" }. Set min_replicas = 1 inside the template block.

Decision Criteria

Decision point Azure Functions Container Apps
Execution duration Up to 10 min (Consumption), 60 min (Premium) Unlimited (container process lifetime)
Runtime flexibility .NET, Node.js, Python, Java, PowerShell Any language, any base image
Scaling trigger HTTP, queue, blob, timer, Event Hub HTTP, KEDA (queue depth, CPU, cron, custom)
Cold start sensitivity Consumption: 1–4 s (.NET/Node), worse Java; Premium: none minReplicas 1 = no cold start; minReplicas 0 = cold start on first request
State and orchestration Durable Functions (built-in) External workflow (Dapr, Service Bus choreography)
VNet integration Premium plan or Flex Consumption Always VNet-capable via managed environment
Operations overhead Near-zero; Azure manages runtime Low; managed Kubernetes, no node management
Cost at low traffic Consumption plan scales to zero Scales to zero only with minReplicas 0; EP1 ~$150/month always-on

Cost Model

Service Free / included Pay-as-you-go
Azure Functions (Consumption) 1M executions/month, 400K GB-seconds $0.20/million executions + $0.000016/GB-second
Azure Functions (Premium EP1) Included vCPU and memory ~$0.173/vCPU-hour; approximately $125–150/month baseline
Container Apps 180K vCPU-seconds, 360K GiB-seconds free/month $0.000024/vCPU-second + $0.000003/GiB-second
API Management (Consumption tier) 1M calls/month $3.50/million calls
Service Bus (Standard) 10M operations/month $0.10/million operations
Cosmos DB (Serverless) None $0.25/million RUs + $0.25/GB

A typical serverless API handling 10M requests per month at 200 ms average duration on Azure Functions Consumption costs approximately $10–18 per month including Service Bus and Cosmos DB serverless.

Cost optimisation levers:

  • Use Consumption plan for background jobs and low-traffic APIs; reserve Premium plan for user-facing endpoints where cold start is a customer-visible latency regression.
  • Set Container Apps minReplicas: 0 for non-interactive workloads (batch processors, async handlers) and minReplicas: 1 for synchronous API paths.
  • Use Cosmos DB Serverless for development and unpredictable-traffic workloads; switch to autoscale RU at sustained load above ~1B RUs/month.
  • Route inter-service traffic through the managed environment VNet rather than the public internet to avoid egress charges.
  • Consolidate Log Analytics workspaces; per-GB ingestion at $2.30/GB makes log verbosity a direct cost lever.

Anti-Patterns to Avoid

⚠ 1. Long-running jobs on Consumption plan Functions

Placing compute-heavy or long-duration operations (report generation, video transcoding, large ETL batches) on Consumption plan Functions hits the 10-minute execution limit, triggers forced termination, and leaves work in an inconsistent state with no native compensation.

Hover to see the fix ↻
↺ Correct Approach

Use Durable Functions for workflows that can be checkpointed across activity steps. For jobs exceeding 60 minutes or requiring custom runtimes, use Container Apps or Azure Container Instances triggered by a Service Bus message.

⚠ 2. Credentials in Function App application settings

Storing database connection strings, API keys, or Service Bus connection strings as plain appSettings values. These appear in the Azure Portal in plain text, are captured in ARM export artefacts, and violate least-privilege access control.

Hover to see the fix ↻
↺ Correct Approach

Use Managed Identity everywhere. Replace AzureWebJobsStorage with AzureWebJobsStorage__accountName (identity-based), replace Service Bus connection strings with ServiceBusConnection__fullyQualifiedNamespace, and reference Key Vault secrets via @Microsoft.KeyVault(SecretUri=...) for any third-party credentials that cannot use identity.

⚠ 3. Scale-to-zero for synchronous user-facing APIs

Setting minReplicas: 0 on Container Apps or using Consumption plan Functions for APIs that serve end users directly. The first request after an idle period incurs a cold start of 1–4 seconds on .NET and Node.js, measurably longer on Java, degrading perceived performance and increasing error rates under traffic spikes.

Hover to see the fix ↻
↺ Correct Approach

Set minReplicas: 1 on Container Apps serving interactive traffic. For Functions, use the Premium plan on user-facing paths and reserve Consumption for background triggers where latency is not customer-visible.

⚠ 4. Logic Apps for complex business rules

Using Logic Apps visual workflows to encode branching business logic, data transformation, or computation. Logic Apps connectors add per-execution cost at scale, the visual editor becomes unmanageable past 20–30 steps, and business logic cannot be unit-tested or version-diffed meaningfully.

Hover to see the fix ↻
↺ Correct Approach

Extract business logic to Azure Functions or Container Apps. Use Logic Apps only for integration plumbing — connecting SaaS systems, triggering on external events, and light data mapping — where its 400+ managed connectors provide genuine value over custom code.

Flowchart

%%{init:{'theme':'base','themeVariables':{'fontSize':'14px','fontFamily':'Inter, system-ui, sans-serif','primaryColor':'#DBEAFE','primaryTextColor':'#1e3a5f','primaryBorderColor':'#2563EB','lineColor':'#374151','clusterBkg':'#F9FAFB','clusterBorder':'#D1D5DB','edgeLabelBackground':'#FFFFFF'},'flowchart':{'curve':'orthogonal','padding':30,'nodeSpacing':65,'rankSpacing':75,'useMaxWidth':true}}}%% flowchart TD CLIENT[Client Application\nWeb, Mobile, or Service] CLIENT --> APIM[API Management\nRate limit, JWT auth\nCaching and routing] APIM --> FUNC[Function App\nPremium EP1 plan\ndotnet-isolated runtime] APIM --> CA[Container Apps\nKEDA autoscaling\nminReplicas 1] APIM --> DURABLE[Durable Functions\nOrchestrator pattern\nFan-out fan-in] FUNC --> SB[Service Bus\nQueues and topics\nAt-least-once delivery] FUNC --> KV[Key Vault\nSecrets via\nManaged Identity] FUNC --> COSMOS[Cosmos DB\nServerless or autoscale\nMulti-region writes] CA --> SB CA --> KV CA --> COSMOS CA --> ACR[Container Registry\nPrivate image store\nIdentity-based pull] DURABLE --> FUNC FUNC --> AI[Application Insights\nDistributed tracing\nLive metrics] CA --> AI AI --> LA[Log Analytics\nKQL dashboards\nAlerting] style CLIENT fill:#4f8ef7,color:#fff style APIM fill:#e0f2fe style DURABLE fill:#e0f2fe style KV fill:#fef3c7 style AI fill:#fef3c7

References

  1. Microsoft — Azure Functions hosting plans. https://learn.microsoft.com/azure/azure-functions/functions-scale
  2. Microsoft — Durable Functions overview. https://learn.microsoft.com/azure/azure-functions/durable/durable-functions-overview
  3. Microsoft — Scale a container app in Azure Container Apps. https://learn.microsoft.com/azure/container-apps/scale-app
  4. Microsoft — Use managed identity for Azure Functions with Azure SQL. https://learn.microsoft.com/azure/azure-functions/functions-identity-access-azure-sql-with-managed-identity
  5. Microsoft — Azure API Management documentation. https://learn.microsoft.com/azure/api-management/
  6. Microsoft — KEDA-based scaling in Container Apps. https://learn.microsoft.com/azure/container-apps/scale-app#scale-triggers
  7. Microsoft — Azure Well-Architected Framework: serverless. https://learn.microsoft.com/azure/architecture/framework/serverless/
  8. Portal: AWS serverless comparison
Ascendion Engineering Knowledge Base ← Cloud