Generated banner for Python Appservice Deploy
Generated logo for Python Appservice Deploy

Python Appservice Deploy

MicrosoftOfficialCreated by Microsoft

  • Skill
  • Workflow
  • Cloud and DevOps
  • MIT
  • Low risk
  • Has scripts

From the author

Deploy Python (Flask/Django/FastAPI) code to Azure App Service Linux. WHEN: "Flask App Service", "Django App Service", "FastAPI App Service", "deploy Python to App Service". DO NOT USE FOR: Container Apps, Functions, non-Python, Terraform/Bicep/IaC, full infra — use azure-prepare.
— the description by Microsoft

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a structured, idempotent workflow for deploying Python applications (Flask, Django, FastAPI) to Azure App Service on Linux. It handles the creation of necessary Azure resources—including Resource Groups, App Service Plans (P0v3), and Web Apps—while enforcing best practices like server-side builds and proper startup configuration. The process includes automated normalization of shell scripts to ensure compatibility and provides a reliable path for both standard CLI-based deployments and those utilizing Azure Developer CLI (azd) configurations.

What it does

  • Automatically creates or validates Azure Resource Groups, App Service Plans, and Web Apps.
  • Configures server-side builds via SCM_DO_BUILD_DURING_DEPLOYMENT.
  • Normalizes Linux shell scripts to prevent line-ending issues during deployment.
  • Sets appropriate startup commands for FastAPI applications.
  • Packages the application code into a clean ZIP archive, excluding unnecessary files.
  • Supports both standard Azure CLI and azd-based deployment workflows.

When to use it

  • When you need to deploy a Python Flask, Django, or FastAPI application to Azure App Service Linux.
  • When you want to automate the provisioning of the required Azure infrastructure alongside the code deployment.
  • When you need a repeatable, error-checked deployment process for Python web apps.
  • When you are working with a standard Python project structure and require a reliable deployment pipeline.

Advantages

  • Reduces manual configuration by deriving sensible defaults for resource names and regions.
  • Ensures cross-platform compatibility by normalizing line endings in scripts.
  • Prevents common deployment failures through pre-packaging validation.
  • Provides a consistent, idempotent approach to resource creation.
  • Integrates seamlessly with existing Azure CLI and azd workflows.

Usage examples

“Deploy my Flask app to Azure App Service.”
The agent will prompt for an app name, then automatically create the necessary Resource Group, P0v3 Linux App Service Plan, and Web App, finally deploying the code.
“Deploy my FastAPI project to Azure.”
The agent will configure the Web App with the required uvicorn startup command and deploy the code to an Azure App Service Linux instance.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/microsoft/python-appservice-deploy/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip python-appservice-deploy.zip -d .agents/skills

Documentation by Microsoft

Python on Azure App Service — Code Deploy

Deploys Python (Flask, Django, FastAPI, generic) code to Azure App Service Linux (P0v3, Python 3.14). Creates RG + Plan + Web App if missing. Hand off to azure-prepare for VNet, Key Vault, databases, or IaC.

MCP tools used: mcp_azure_mcp_subscription_list, mcp_azure_mcp_group_list, mcp_azure_mcp_appservice, mcp_azure_mcp_azd (when azure.yaml is present).

Workflow

Prerequisite: Verify the latest stable Azure CLI; update with approval and recheck if needed.

  1. Resolve context — smart defaults, minimal prompts. Only the app name is interactive; RG (<app>-rg), Plan (<app>-plan), region (current az default or eastus2), subscription are derived. create-app.md §1.

  2. Detect framework (advisory, never blocks). detect.md.

  3. Choose path — azure.yaml host: appservice → deploy-azd.md; else deploy-azcli.md.

  4. Ensure RG → Plan (P0v3 --is-linux) → Web App (--runtime "PYTHON:3.14") exist. Before creating a plan, invoke azure-quotas to check quota availability. On transient ARM errors, follow transient-retry.md. create-app.md.

  5. Set startup — Flask/Django: none (Oryx auto-detects). FastAPI: always python -m uvicorn main:app --host 0.0.0.0. Other: warn. startup-commands.md.

  6. Set SCM_DO_BUILD_DURING_DEPLOYMENT=true.

  7. Deploy — azd deploy or az webapp deploy --type zip --track-status false --enriched-errors true.

  8. STOP. Print the post-deploy message (post-deploy-message.md) and end the turn.

Hard rules
  • LINUX SCRIPTS — normalize selected shell/startup files to LF, then verify no CR bytes remain. Stop on normalization, validation or packaging failure; deploy only a fresh package.

  • DJANGO REMOTE BUILDS — require STATIC_ROOT and successful system/collectstatic checks before packaging.

  • ⛔ NO POST-DEPLOY VERIFICATION — after deploy returns, do not run az webapp log tail, curl, Invoke-WebRequest, or any health probe. App Service needs 2–3 min to warm; a quiet log or early 5xx is not failure.

  • ⛔ SHELL SAFETY — for --runtime always use "PYTHON:3.14" (colon). Never "PYTHON|3.14" (pipe is a shell operator).

  • ⛔ NEVER az webapp up — deprecated. Use Step 7 commands.

  • ✅ URL FORMAT — present endpoints as https://... URLs.

Error Handling

See errors.md for the full symptom → cause → fix matrix. Quick triage: missing plan/app → re-run Step 4; container ping timeout on 8000 → fix startup (Step 5); ModuleNotFoundError after deploy → ensure Step 6 ran, redeploy.

Files

references/deploy-azcli.md

Deploy via az CLI

Use this path when there is no azure.yaml or it doesn't target appservice.

⛔ NEVER USE az webapp up — this command is deprecated. Use the explicit create + deploy commands below.

Prerequisites

  • Verify the latest stable Azure CLI is installed (installation/update guide).

  • az login complete

  • Subscription, resource group, region, and app name decided (see create-app.md)

  • App Service Plan (Linux, P0v3) and Web App (Python runtime) exist (see create-app.md)

1. Enable server-side build

az webapp config appsettings set \
  -n <app> -g <rg> \
  --settings SCM_DO_BUILD_DURING_DEPLOYMENT=true
az webapp config appsettings set `
  -n <app> -g <rg> `
  --settings SCM_DO_BUILD_DURING_DEPLOYMENT=true

This tells Oryx to run pip install -r requirements.txt during deploy.

2. Startup command — skip for Flask/Django, always set for FastAPI

Azure App Service (Oryx) auto-detects Flask and Django — do not set a startup command for these. Skip this step entirely.

For FastAPI, always set the uvicorn startup command, regardless of the Python runtime version. This skill does not rely on Oryx FastAPI auto-detection, so the behavior is identical on every supported runtime (3.12, 3.13, 3.14, …):

az webapp config set -n <app> -g <rg> \
  --startup-file "python -m uvicorn main:app --host 0.0.0.0"
az webapp config set -n <app> -g <rg> `
  --startup-file "python -m uvicorn main:app --host 0.0.0.0"

(Replace main:app if the FastAPI entry point differs — e.g., app.main:app.)

For other frameworks (generic WSGI / ASGI / unknown), skip this step and emit the manual-startup warning. See startup-commands.md.

3. Package the code

For Django remote builds, require STATIC_ROOT and successful python manage.py check and python manage.py collectstatic --noinput before packaging.

Normalize Linux scripts

Before every deployment/redeployment, explicitly select shipped .sh files, text executables with a shebang, and referenced startup scripts. Exclude binaries and dependency folders. From the application/service root, replace the example path below with the selected files; skip only if none exist.

This command works in Bash and PowerShell, preserves encoding/permissions, normalizes CRLF/lone CR, then re-reads bytes to verify LF:

python -c "import sys; from pathlib import Path
for name in sys.argv[1:]:
    p = Path(name)
    data = p.read_bytes()
    if b'\0' in data: raise ValueError(p)
    lf = data.replace(b'\r\n', b'\n').replace(b'\r', b'\n')
    if lf != data: p.write_bytes(lf)
    if b'\r' in p.read_bytes(): raise ValueError(p)
" "./startup.sh"

Immediately check the result in the same shell call before packaging:

if [ "$?" -ne 0 ]; then exit 1; fi
if (-not $? -or $LASTEXITCODE -ne 0) { throw "LF normalization/validation failed" }
Create a fresh ZIP

Package only after normalization/validation succeeds. Stop on any failure; deployment requires successful packaging, not merely an existing archive.

Zip the project (excluding venv, caches, git, node_modules):

# bash
rm -f app.zip || exit 1
zip -r app.zip . \
  -x ".git/*" -x ".venv/*" -x "venv/*" -x "__pycache__/*" \
  -x "*.pyc" -x ".env" -x "node_modules/*" -x "app.zip" \
  || { rm -f app.zip; exit 1; }
# PowerShell
if (Test-Path app.zip) { Remove-Item app.zip -ErrorAction Stop }
$exclude = @('.git','.venv','venv','__pycache__','node_modules','app.zip')
$items = Get-ChildItem -Force -ErrorAction Stop | Where-Object { $exclude -notcontains $_.Name }
try {
    Compress-Archive -Path $items -DestinationPath app.zip -Force -ErrorAction Stop
} catch {
    if (Test-Path app.zip) { Remove-Item app.zip -ErrorAction Stop }
    throw
}

4. Deploy the zip

Proceed only if normalization, validation and ZIP creation all succeeded in this attempt. Never deploy a stale/partial archive or continue after a failed packaging call.

az webapp deploy \
  -n <app> -g <rg> \
  --src-path app.zip \
  --type zip \
  --track-status false \
  --enriched-errors true
az webapp deploy `
  -n <app> -g <rg> `
  --src-path app.zip `
  --type zip `
  --track-status false `
  --enriched-errors true

Do not use --only-show-errors or suppress enriched diagnostics. Report failures with suggested fixes and the Copilot prompt, not success.

💡 --track-status false returns once the ZIP is accepted by the SCM endpoint — this is not the same as "Oryx build succeeded". The server-side pip install / startup-command rendering happens asynchronously after the CLI returns. A zero exit code only confirms the upload + a deployment record. If the site never starts, inspect the build outcome via az webapp log deployment list/show — that is the only authoritative confirmation that the build itself succeeded.

5. Stop. Report the endpoint to the user.

After az webapp deploy returns, the skill is done.

ℹ️ az webapp deploy does not initiate a cold start by pinging the site. With --track-status false, it returns as soon as the SCM endpoint accepts the ZIP; the Oryx build and container restart happen asynchronously on the SCM side. The container only warms up when an inbound HTTP request actually hits https://<app>.azurewebsites.net — which is why the post-deploy message tells the user to expect a 2–3 minute wait on their first visit.

⛔ Do NOT run az webapp log tail, curl, Invoke-WebRequest, wget, or any other "verify startup" command. App Service routinely needs 2–3 minutes to warm the container; a quiet log stream or a 5xx in the first couple of minutes is not a failure signal, and running these probes here will mislead the user.

Resolve the host name without hitting the site:

HOST=$(az webapp show -n <app> -g <rg> --query defaultHostName -o tsv)
echo "https://$HOST"
$host_ = az webapp show -n <app> -g <rg> --query defaultHostName -o tsv
"https://$host_"

Then print the post-deploy message from post-deploy-message.md and end the turn. The user will run az webapp log tail -n <app> -g <rg> themselves if they want to watch logs.

Common pitfalls

PitfallFix
Deployed code missing dependenciesSCM_DO_BUILD_DURING_DEPLOYMENT=true not set — re-run step 1 then redeploy
Container ping timeout on port 8000Wrong startup command — see startup-commands.md
Zip too large (>500 MB)Exclude .venv, caches; consider .deployment .gitignore-style file
webapp up examples in older docsReplace with az webapp create + az webapp deploy (this file)

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs Python

Security

What our scanner noticed· Low risk

  • Instructions that use the network
  • Scripts that your agent may run

This skill includes scripts that your agent may run. Read them before you use it.

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
Microsoft
Publisher
Microsoft (Official)
Source
microsoft/skills · .github/plugins/azure-skills/skills/python-appservice-deploy · commit 0696600
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from .github/plugins/azure-skills/LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced
Also distributed in
microsoft/azure-skills

AutoGineer is not affiliated with or endorsed by Microsoft or Microsoft.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download python-appservice-deploy.zip