02 - First Deploy (Premium)¶
Deploy a Python Function App to an Elastic Premium plan (EP1) with VNet integration and private endpoint support, then publish code and verify the app is live.
Prerequisites¶
- You completed 01 - Run Locally.
- You are signed in to Azure CLI and have Contributor access.
- You already exported:
$RG,$APP_NAME,$PLAN_NAME,$STORAGE_NAME,$LOCATION(usekoreacentralfor this guide).
What You'll Build¶
- A Linux Python Function App on Elastic Premium (
EP1) with runtime settings. - VNet integration and a site private endpoint for private inbound access.
- A first deployment pipeline (
func azure functionapp publish) and endpoint verification.
Infrastructure Context
Plan: Premium (EP1) | Network: VNet + Private Endpoints | Always warm: ✅
Premium deploys with VNet integration (delegated subnet), a private endpoint for inbound access, private DNS zone, and pre-warmed instances. Storage uses connection string or identity-based authentication.
flowchart TD
INET[Internet] -->|HTTPS| FA[Function App\nPremium EP1\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\nallowPublicAccess: false\nallowSharedKeyAccess: true"]
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]
subgraph STORAGE[Content Backend]
SHARE[Azure Files\ncontent share]
end
ST --- SHARE
WARM["🔥 Pre-warmed instances\nMin: 1, Max: 20-100"] -.- FA
style FA fill:#ff8c00,color:#fff
style VNET fill:#E8F5E9,stroke:#4CAF50
style ST fill:#FFF3E0
style DNS fill:#E3F2FD
style WARM fill:#FFF3E0,stroke:#FF9800 Steps¶
-
Authenticate and set subscription context.
Expected output (abridged):
-
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" \ --allow-blob-public-access falseExpected output (abridged):
-
Create the Premium plan and Function App (Linux example).
az functionapp plan create \ --name "$PLAN_NAME" \ --resource-group "$RG" \ --location "$LOCATION" \ --sku "EP1" \ --is-linux 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"
Globally unique names required
Both $APP_NAME and $STORAGE_NAME must be globally unique across all Azure subscriptions. If you get a naming conflict, append a random suffix (e.g., func-prem-04091234).
Expected output (abridged):
Enterprise policy: Shared key access
Some enterprise subscriptions enforce Azure Policy that sets allowSharedKeyAccess: false on all storage accounts. Premium (EP1) requires WEBSITE_CONTENTAZUREFILECONNECTIONSTRING with a connection string that uses shared key access to create the content file share during provisioning. If your subscription has this policy, the Function App creation will fail with a 403 error. Solutions:
- Request a policy exemption from your Azure administrator
- Use Flex Consumption (FC1) which supports identity-based blob storage without shared keys
- Use Dedicated (B1) which uses
WEBSITE_RUN_FROM_PACKAGEwithout a content file share
-
Configure app settings using classic
siteConfig.appSettingsmodel values.az functionapp config appsettings set \ --name "$APP_NAME" \ --resource-group "$RG" \ --settings \ "FUNCTIONS_WORKER_RUNTIME=python" \ "AzureWebJobsStorage__accountName=$STORAGE_NAME" \ "AzureWebJobsStorage__credential=managedidentity"Expected output (abridged):
[ { "name": "FUNCTIONS_WORKER_RUNTIME", "value": "python" }, { "name": "AzureWebJobsStorage__accountName", "value": "<storage-account-name>" }, { "name": "AzureWebJobsStorage__credential", "value": "managedidentity" } ]For Premium, both host-storage models are valid: - Connection string:
AzureWebJobsStorage=<connection-string>- Identity-based:AzureWebJobsStorage__accountName=<storage-account-name>plusAzureWebJobsStorage__credential=managedidentity -
Enable a system-assigned managed identity for the Function App.
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)Expected output (abridged):
-
Assign storage RBAC roles to the managed identity.
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" az role assignment create \ --assignee "$MI_PRINCIPAL_ID" \ --role "Storage File Data Privileged Contributor" \ --scope "$STORAGE_ID"Expected output (abridged):
/subscriptions/<subscription-id>/resourceGroups/<resource-group-name>/providers/Microsoft.Storage/storageAccounts/<storage-account-name>{ "principalId": "<object-id>", "roleDefinitionName": "Storage Blob Data Owner", "scope": "/subscriptions/<subscription-id>/resourceGroups/<resource-group-name>/providers/Microsoft.Storage/storageAccounts/<storage-account-name>" }{ "principalId": "<object-id>", "roleDefinitionName": "Storage Account Contributor", "scope": "/subscriptions/<subscription-id>/resourceGroups/<resource-group-name>/providers/Microsoft.Storage/storageAccounts/<storage-account-name>" }{ "principalId": "<object-id>", "roleDefinitionName": "Storage Queue Data Contributor", "scope": "/subscriptions/<subscription-id>/resourceGroups/<resource-group-name>/providers/Microsoft.Storage/storageAccounts/<storage-account-name>" }{ "principalId": "<object-id>", "roleDefinitionName": "Storage File Data Privileged Contributor", "scope": "/subscriptions/<subscription-id>/resourceGroups/<resource-group-name>/providers/Microsoft.Storage/storageAccounts/<storage-account-name>" }Why these four roles are required on Premium
Premium Function Apps need host storage and Azure Files content share access during provisioning and runtime:
Storage Blob Data Ownerfor host blobs, leases, and trigger state.Storage Account Contributorfor storage account-level management operations used by the platform.Storage Queue Data Contributorfor queue-backed host coordination and trigger operations.Storage File Data Privileged Contributorfor the Azure Files content share (WEBSITE_CONTENTAZUREFILECONNECTIONSTRING) used by Premium.
-
Create a VNet with separate subnets for integration and private endpoints.
az network vnet create \ --name "vnet-premium-demo" \ --resource-group "$RG" \ --location "$LOCATION" \ --address-prefixes "10.20.0.0/16" \ --subnet-name "snet-integration" \ --subnet-prefixes "10.20.1.0/24" az network vnet subnet create \ --name "snet-private-endpoints" \ --resource-group "$RG" \ --vnet-name "vnet-premium-demo" \ --address-prefixes "10.20.2.0/24" az network vnet subnet update \ --name "snet-integration" \ --resource-group "$RG" \ --vnet-name "vnet-premium-demo" \ --delegations "Microsoft.Web/serverFarms" az functionapp vnet-integration add \ --name "$APP_NAME" \ --resource-group "$RG" \ --vnet "vnet-premium-demo" \ --subnet "snet-integration" 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-premium-demo" \ --subnet "snet-private-endpoints" \ --private-connection-resource-id "$STORAGE_ID" \ --group-ids "$SVC" \ --connection-name "conn-st-$SVC" doneExpected output (abridged):
-
Create a private endpoint for inbound private access.
APP_ID=$(az functionapp show \ --name "$APP_NAME" \ --resource-group "$RG" \ --query "id" \ --output tsv) az network private-endpoint create \ --name "pe-$APP_NAME" \ --resource-group "$RG" \ --location "$LOCATION" \ --vnet-name "vnet-premium-demo" \ --subnet "snet-private-endpoints" \ --private-connection-resource-id "$APP_ID" \ --group-ids "sites" \ --connection-name "conn-$APP_NAME"Expected output (abridged):
/subscriptions/<subscription-id>/resourceGroups/<resource-group-name>/providers/Microsoft.Web/sites/<function-app-name>{ "name": "pe-<function-app-name>", "provisioningState": "Succeeded", "privateLinkServiceConnections": [ { "groupIds": [ "sites" ] } ] }Private endpoint name resolution requires private DNS configuration. At minimum, create and link
privatelink.azurewebsites.netto the VNet, then attach the zone to the private endpoint:az network private-dns zone create \ --resource-group "$RG" \ --name "privatelink.azurewebsites.net" az network private-dns link vnet create \ --resource-group "$RG" \ --zone-name "privatelink.azurewebsites.net" \ --name "link-vnet-premium-demo" \ --virtual-network "vnet-premium-demo" \ --registration-enabled false az network private-endpoint dns-zone-group create \ --resource-group "$RG" \ --endpoint-name "pe-$APP_NAME" \ --name "web-dns-zone-group" \ --private-dns-zone "privatelink.azurewebsites.net" \ --zone-name "web-config" 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-premium-demo" \ --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" doneExpected output (abridged):
{ "name": "privatelink.blob.core.windows.net", "provisioningState": "Succeeded" } { "name": "privatelink.queue.core.windows.net", "provisioningState": "Succeeded" } { "name": "privatelink.table.core.windows.net", "provisioningState": "Succeeded" } { "name": "privatelink.file.core.windows.net", "provisioningState": "Succeeded" } -
Publish function code (Premium supports file share-based deployment and SCM/Kudu).
Expected output (abridged):
-
Verify app status and endpoint.
az functionapp show \ --name "$APP_NAME" \ --resource-group "$RG" \ --output table curl --request GET "https://$APP_NAME.azurewebsites.net/api/health"Expected output (abridged):
Auto-created Application Insights
az functionapp create automatically creates an Application Insights resource and links it via APPINSIGHTS_INSTRUMENTATIONKEY and APPLICATIONINSIGHTS_CONNECTION_STRING. Tutorial 04 creates a second, explicit Application Insights resource. You may see duplicate resources — this is expected.
Verification¶
Expected output when policy allows shared key access¶
{
"id": "/subscriptions/<subscription-id>/resourceGroups/rg-func-premium-demo/providers/Microsoft.Web/sites/func-premium-demo",
"location": "koreacentral",
"name": "func-premium-demo",
"state": "Running",
"defaultHostName": "func-premium-demo.azurewebsites.net"
}
Getting site publishing info...
Creating archive for current directory...
Uploading 14.8 MB [########################################]
Upload completed successfully.
Deployment completed successfully.
Syncing triggers...
Functions in func-premium-demo:
health - [httpTrigger]
info - [httpTrigger]
Deployment Verification Results¶
Blocked by enterprise policy
In our Korea Central deployment, EP1 Premium was blocked during provisioning by the same allowSharedKeyAccess: false policy as Consumption. Premium plans also require WEBSITE_CONTENTAZUREFILECONNECTIONSTRING with shared key access for the content file share.
Observed error:
ERROR: Creation of storage file share failed with: 'The remote server returned an error: (403) Forbidden.'.
Please check if the storage account is accessible.
Workarounds:
- Request a policy exemption from your Azure administrator
- Use Flex Consumption (FC1) which supports identity-based blob storage
- Use Dedicated (B1) which uses
WEBSITE_RUN_FROM_PACKAGEwithout a content file share
flowchart LR
A[Internet] --> B[Private Endpoint]
B --> C[Function App Premium EP1]
C --> D[VNet Integration Subnet]
D --> E[Private Backend]
C --> F[Pre-warmed Instances] Next Steps¶
Next: 03 - Configuration
See Also¶
- Tutorial Overview & Plan Chooser
- Python Language Guide
- Platform: Hosting Plans
- Operations: Deployment
- Recipes Index