Skip to content

02 - First Deploy (Dedicated)

In this tutorial you deploy the Function App to a Dedicated App Service Plan using Basic B1. Dedicated plans are always running (no scale-to-zero), support Linux and Windows, and use fixed monthly pricing regardless of executions.

Prerequisites

export RG="rg-func-dedicated-dev"
export APP_NAME="func-dedi-<unique-suffix>"
export PLAN_NAME="asp-dedi-b1-dev"
export STORAGE_NAME="stdedidev<unique>"
export LOCATION="koreacentral"
Command/Parameter Purpose
export RG="rg-func-dedicated-dev" Defines the resource group name for the deployment.
export APP_NAME="func-dedi-<unique-suffix>" Sets the unique name for the Azure Function App.
export PLAN_NAME="asp-dedi-b1-dev" Defines the App Service Plan name.
export STORAGE_NAME="stdedidev<unique>" Sets the storage account name.
export LOCATION="koreacentral" Specifies the Azure region for deployment.

What You'll Build

You will provision a Basic (B1) Linux App Service Plan, create a Python Function App on that plan, deploy from apps/python, and validate live endpoints.

Network Scenario Choices

This tutorial deploys with public networking on B1. Basic supports App Service VNet integration and private endpoints, but this guide uses Standard (S1+) for private networking walkthroughs to match the production-oriented validation path.

Scenario Description Guide
Public Only No VNet (this tutorial, B1) Current page
Private Egress VNet + Storage PE (guide validates S1+) Private Egress
Private Ingress + Site Private Endpoint (guide validates S1+) Private Ingress
Fixed Outbound IP + NAT Gateway (guide validates S1+) Fixed Outbound

Infrastructure Context

Plan: Dedicated (B1) | Network: Public internet in this tutorial | VNet: Supported by platform, not configured here

The app runs on a fixed App Service Plan (always on, no scale-to-zero). Basic B1 supports App Service VNet integration and private endpoints, but this guide uses Standard (S1+) for private networking scenarios to provide scale headroom, deployment slots, and a production-oriented validation path.

flowchart TD
    INET[Internet] -->|HTTPS| FA[Function App\nDedicated B1-P3v3\nLinux Python 3.11]

    subgraph VNET["VNet 10.0.0.0/16"]
        subgraph INT_SUB["Integration Subnet 10.0.1.0/24\nDelegation: Microsoft.Web/serverFarms"]
            FA
        end
        subgraph PE_SUB["Private Endpoint Subnet 10.0.2.0/24"]
            PE_BLOB[PE: blob]
            PE_QUEUE[PE: queue]
            PE_TABLE[PE: table]
            PE_FILE[PE: file]
        end
    end

    PE_BLOB --> ST["Storage Account"]
    PE_QUEUE --> ST
    PE_TABLE --> ST
    PE_FILE --> ST

    subgraph DNS[Private DNS Zones]
        DNS_BLOB[privatelink.blob.core.windows.net]
        DNS_QUEUE[privatelink.queue.core.windows.net]
        DNS_TABLE[privatelink.table.core.windows.net]
        DNS_FILE[privatelink.file.core.windows.net]
    end

    PE_BLOB -.-> DNS_BLOB
    PE_QUEUE -.-> DNS_QUEUE
    PE_TABLE -.-> DNS_TABLE
    PE_FILE -.-> DNS_FILE

    FA -.->|System-Assigned MI| ENTRA[Microsoft Entra ID]
    FA --> AI[Application Insights]

    RFP["📦 WEBSITE_RUN_FROM_PACKAGE=1\nNo content share required"] -.- FA
    ALWAYS_ON["⚙️ Always On: true\nFixed capacity"] -.- FA

    style FA fill:#5c2d91,color:#fff
    style VNET fill:#E8F5E9,stroke:#4CAF50
    style ST fill:#FFF3E0
    style DNS fill:#E3F2FD

Steps

Step 1 - Create resource group and storage account

az group create \
  --name $RG \
  --location $LOCATION

az storage account create \
  --name $STORAGE_NAME \
  --resource-group $RG \
  --location $LOCATION \
  --sku Standard_LRS \
  --kind StorageV2
Command/Parameter Purpose
az group create Provisions a new resource group.
--name $RG Sets the resource group name.
--location $LOCATION Places the resource group in the selected region.
az storage account create Creates a new storage account for the function app.
--sku Standard_LRS Selects Standard Locally Redundant Storage.
--kind StorageV2 Specifies the storage account type as General Purpose v2.

Step 2 - Create a Dedicated App Service Plan (B1)

az appservice plan create \
  --name $PLAN_NAME \
  --resource-group $RG \
  --location $LOCATION \
  --sku B1 \
  --is-linux
Command/Parameter Purpose
az appservice plan create Provisions a Dedicated App Service plan.
--sku B1 Selects the Basic B1 pricing tier.
--is-linux Configures the plan for Linux hosting.

For production workloads, use S1 or P1v2 when you need higher scale limits, autoscale rules, deployment slots, or the same private networking path validated by this guide.

Step 3 - Create the Function App on the plan

az functionapp create \
  --name $APP_NAME \
  --resource-group $RG \
  --plan $PLAN_NAME \
  --storage-account $STORAGE_NAME \
  --runtime python \
  --runtime-version 3.11 \
  --functions-version 4 \
  --os-type Linux
Command/Parameter Purpose
az functionapp create Provisions the function app within the Dedicated plan.
--plan $PLAN_NAME Links the app to the specific App Service plan.
--runtime python Sets the language runtime to Python.
--runtime-version 3.11 Specifies the Python version.
--functions-version 4 Selects version 4.x of the runtime.
--os-type Linux Deploys the app on a Linux host.
az functionapp config set \
  --name $APP_NAME \
  --resource-group $RG \
  --always-on true
Command/Parameter Purpose
az functionapp config set Modifies configuration settings for the function app.
--always-on true Keeps the function host loaded to prevent cold start delays.

Step 5 - Deploy code

cd apps/python
func azure functionapp publish $APP_NAME --python
Command/Parameter Purpose
cd apps/python Navigates to the project directory.
func azure functionapp publish $APP_NAME Deploys the local project to Azure.
--python Sets the application language.

Dedicated deployment here uses remote Oryx build via func azure functionapp publish --python with WEBSITE_RUN_FROM_PACKAGE=1. Unlike Consumption and Premium content share scenarios, B1 does not require WEBSITE_CONTENTAZUREFILECONNECTIONSTRING.

Set placeholder trigger settings before first request

The reference app includes EventHub, Queue, and Timer triggers that need connection settings. If these are missing, the function host may report errors. Set placeholder values immediately after first deploy:

az functionapp config appsettings set \
  --name $APP_NAME \
  --resource-group $RG \
  --settings \
    EventHubConnection="Endpoint=sb://placeholder.servicebus.windows.net/;SharedAccessKeyName=placeholder;SharedAccessKey=placeholder=;EntityPath=placeholder" \
    QueueStorage="UseDevelopmentStorage=true" \
    TIMER_LAB_SCHEDULE="0 0 0 1 1 *"

az functionapp restart --name $APP_NAME --resource-group $RG
Command/Parameter Purpose
az functionapp config appsettings set Configures key-value pairs in the environment.
--settings "..." Sets trigger-specific placeholder settings.
az functionapp restart Restarts the function app to apply configuration changes.

After restart, wait 60–90 seconds for the host to become ready on B1 tier.

Step 6 - Verify deployment

az functionapp show \
  --name $APP_NAME \
  --resource-group $RG \
  --query "{state:state,defaultHostName:defaultHostName,kind:kind}" \
  --output json

curl --request GET "https://$APP_NAME.azurewebsites.net/api/health"
Command/Parameter Purpose
az functionapp show Retrieves current status of the app.
--query "{...}" Selects relevant status fields.
curl --request GET Tests the public HTTP health endpoint.
flowchart TD
    A[Internet] --> B[Function App\nLinux, Python 3.11]
    B --> C[App Service Plan B1\nAlways Running]
    B --> D[Azure Services\nStorage, Monitor, App Insights]

B1 network support and guide scope

Basic (B1) supports App Service VNet integration and private endpoints, but this tutorial intentionally remains public-only. Use the optional section when you want to test private networking; use Standard (S1+) if you want to match the guide-tested production path.

Optional: VNet and Private Endpoints

Optional: VNet and Private Endpoints

Basic (B1) can use these platform networking features. This walkthrough upgrades to S1 to align with the guide-tested private networking path and provide more production headroom.

az appservice plan update \
  --name $PLAN_NAME \
  --resource-group $RG \
  --sku S1
Command/Parameter Purpose
az appservice plan update Modifies plan properties.
--sku S1 Upgrades the tier to Standard S1 for the guide-tested private networking path.

Step B: Create VNet and Subnets

export VNET_NAME="vnet-dedicated-demo"

az network vnet create \
  --name "$VNET_NAME" \
  --resource-group "$RG" \
  --location "$LOCATION" \
  --address-prefixes "10.0.0.0/16" \
  --subnet-name "snet-integration" \
  --subnet-prefixes "10.0.1.0/24"

az network vnet subnet create \
  --name "snet-private-endpoints" \
  --resource-group "$RG" \
  --vnet-name "$VNET_NAME" \
  --address-prefixes "10.0.2.0/24"

az network vnet subnet update \
  --name "snet-integration" \
  --resource-group "$RG" \
  --vnet-name "$VNET_NAME" \
  --delegations "Microsoft.Web/serverFarms"
Command/Parameter Purpose
az network vnet create Provisions a new virtual network.
az network vnet subnet create Adds a subnet for private endpoints.
az network vnet subnet update Configures the integration subnet delegation.

Step C: Enable VNet Integration

az functionapp vnet-integration add \
  --name "$APP_NAME" \
  --resource-group "$RG" \
  --vnet "$VNET_NAME" \
  --subnet "snet-integration"
Command/Parameter Purpose
az functionapp vnet-integration add Connects the app to the virtual network.

Step D: Enable System-Assigned Managed Identity

az functionapp identity assign \
  --name "$APP_NAME" \
  --resource-group "$RG"

export MI_PRINCIPAL_ID=$(az functionapp identity show \
  --name "$APP_NAME" \
  --resource-group "$RG" \
  --query "principalId" \
  --output tsv)
Command/Parameter Purpose
az functionapp identity assign Enables managed identity for the app.
az functionapp identity show Retrieves the app's identity properties.

Step E: Assign RBAC Roles

export STORAGE_ID=$(az storage account show \
  --name "$STORAGE_NAME" \
  --resource-group "$RG" \
  --query "id" \
  --output tsv)

az role assignment create \
  --assignee "$MI_PRINCIPAL_ID" \
  --role "Storage Blob Data Owner" \
  --scope "$STORAGE_ID"

az role assignment create \
  --assignee "$MI_PRINCIPAL_ID" \
  --role "Storage Account Contributor" \
  --scope "$STORAGE_ID"

az role assignment create \
  --assignee "$MI_PRINCIPAL_ID" \
  --role "Storage Queue Data Contributor" \
  --scope "$STORAGE_ID"
Command/Parameter Purpose
az role assignment create Grants specified permissions to the managed identity.
--role "..." Selects a specific storage data or contributor role.

RBAC roles explained

  • Storage Blob Data Owner: Read/write blob data (used by the Functions runtime for triggers, bindings, and internal state)
  • Storage Account Contributor: Manage storage account properties
  • Storage Queue Data Contributor: Read/write queue messages (used by durable functions, queue triggers)

Step F: Lock Down Storage

az storage account update \
  --name "$STORAGE_NAME" \
  --resource-group "$RG" \
  --allow-blob-public-access false
Command/Parameter Purpose
az storage account update Modifies storage properties.
--allow-blob-public-access false Disables public anonymous access.

Step G: Create Storage Private Endpoints (×4)

for SVC in blob queue table file; do
  az network private-endpoint create \
    --name "pe-st-$SVC" \
    --resource-group "$RG" \
    --location "$LOCATION" \
    --vnet-name "$VNET_NAME" \
    --subnet "snet-private-endpoints" \
    --private-connection-resource-id "$STORAGE_ID" \
    --group-ids "$SVC" \
    --connection-name "conn-st-$SVC"
done
Command/Parameter Purpose
az network private-endpoint create Provisions private endpoints for each storage sub-resource.
for SVC in blob queue table file; do
  az network private-dns zone create \
    --resource-group "$RG" \
    --name "privatelink.$SVC.core.windows.net"

  az network private-dns link vnet create \
    --resource-group "$RG" \
    --zone-name "privatelink.$SVC.core.windows.net" \
    --name "link-$SVC" \
    --virtual-network "$VNET_NAME" \
    --registration-enabled false

  az network private-endpoint dns-zone-group create \
    --resource-group "$RG" \
    --endpoint-name "pe-st-$SVC" \
    --name "$SVC-dns-zone-group" \
    --private-dns-zone "privatelink.$SVC.core.windows.net" \
    --zone-name "$SVC"
done
Command/Parameter Purpose
az network private-dns zone create Provisions private DNS zones for storage services.
az network private-dns link vnet create Links DNS zones to the virtual network.
az network private-endpoint dns-zone-group create Configures automatic IP registration in DNS.

Step I: Configure Identity-Based Storage

az functionapp config appsettings set \
  --name "$APP_NAME" \
  --resource-group "$RG" \
  --settings \
    "AzureWebJobsStorage__accountName=$STORAGE_NAME" \
    "AzureWebJobsStorage__credential=managedidentity"
Command/Parameter Purpose
az functionapp config appsettings set Configures identity-based storage access.

Step J: Verify VNet Integration

az functionapp show \
  --name "$APP_NAME" \
  --resource-group "$RG" \
  --query "virtualNetworkSubnetId" \
  --output tsv
Command/Parameter Purpose
az functionapp show Retrieves current VNet integration details.

Verification

az appservice plan create ... --sku B1:

{
  "id": "/subscriptions/<subscription-id>/resourceGroups/rg-func-dedicated-dev/providers/Microsoft.Web/serverfarms/asp-dedi-b1-dev",
  "kind": "linux",
  "location": "koreacentral",
  "name": "asp-dedi-b1-dev",
  "resourceGroup": "rg-func-dedicated-dev",
  "sku": {
    "name": "B1",
    "tier": "Basic"
  },
  "status": "Ready"
}

az functionapp config set --always-on true:

{
  "alwaysOn": true,
  "linuxFxVersion": "Python|3.11",
  "numberOfWorkers": 1,
  "scmType": "None"
}

curl --request GET "https://$APP_NAME.azurewebsites.net/api/health":

{
  "status": "healthy",
  "timestamp": "2026-04-04T05:38:46Z",
  "version": "1.0.0"
}

Deployment Verification Results

Endpoint test results from the Korea Central deployment (all returned HTTP 200):

  • GET /api/health{"status": "healthy", "timestamp": "2026-04-04T05:38:46Z", "version": "1.0.0"}
  • GET /api/info{"name": "azure-functions-field-guide", "version": "1.0.0", "python": "3.11.13", "environment": "development", "telemetryMode": "basic"}
  • GET /api/requests/log-levels{"message": "Logged at all levels", "levels": ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]}
  • GET /api/dependencies/external{"status": "success", "statusCode": 200, "responseTime": "1143ms", "url": "https://httpbin.org/get"}
  • GET /api/exceptions/test-error{"error": "Handled exception", "type": "ValueError", "message": "Simulated error for testing"}

Next Steps

Your first Dedicated deployment is live. Next you will configure app settings, storage options, and runtime behavior with siteConfig.appSettings conventions.

Next: 03 - Configuration

See Also

Sources