Quick takeaway: Azure Functions lets you run Python code in the cloud without managing servers. This guide walks you through creating, testing, and deploying your first Azure Function — with working code examples for HTTP triggers, timer triggers, and Blob Storage bindings. You’ll also learn how to avoid the most common cold-start trap.
Prerequisites
Before you start, make sure you have these tools installed and configured:
- Python 3.10 or later —
python3 --version - Azure CLI — install guide, then
az login - Azure Functions Core Tools —
npm install -g azure-functions-core-tools@4orbrew install azure-functions-core-tools@4 - An active Azure subscription — the free tier gives you 1 million executions/month
Create Your First Function
Start by scaffolding a new Functions project. Open a terminal and run:
mkdir azure-functions-demo && cd azure-functions-demo
func init --python
func new --name HttpTriggerDemo --template "HTTP trigger"Code language: JavaScript (javascript)
This creates a function.json (trigger configuration) and __init__.py (your code). The default template generates an HTTP-triggered function that reads a name parameter from the query string or request body.
Run it locally to verify:
func start
You should see output ending with a local URL like http://localhost:7071/api/HttpTriggerDemo. Test it:
curl "http://localhost:7071/api/HttpTriggerDemo?name=Reponotes"Code language: JavaScript (javascript)
Expected response: Hello, Reponotes. This HTTP triggered function executed successfully.
HTTP Trigger: Code Walkthrough
Open HttpTriggerDemo/__init__.py. Here is what matters and why:
import azure.functions as func
import logging
def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
name = req.params.get('name')
if not name:
try:
req_body = req.get_json()
name = req_body.get('name')
except ValueError:
pass
if name:
return func.HttpResponse(
f"Hello, {name}. This HTTP triggered function executed successfully."
)
else:
return func.HttpResponse(
"Pass a name in the query string or in the request body.",
status_code=400
)Code language: JavaScript (javascript)
Key things to notice:
req.params.get('name')reads query-string parameters first.- If the parameter is missing, it falls back to parsing the JSON body.
- Return a
func.HttpResponsewith a status code — 200 for success, 400 when the required parameter is missing.
Timer Trigger: Run Code on a Schedule
Timer triggers are useful for periodic cleanup jobs, data aggregation, or health checks. Create one:
func new --name TimerDemo --template "Timer trigger"Code language: JavaScript (javascript)
Open the generated TimerDemo/function.json. The schedule field uses standard CRON expressions:
{
"scriptFile": "__init__.py",
"bindings": [
{
"name": "mytimer",
"type": "timerTrigger",
"direction": "in",
"schedule": "0 */5 * * * *"
}
]
}Code language: JSON / JSON with Comments (json)
This schedule means “run every 5 minutes.” The CRON format for Azure Functions has 6 fields — the extra field at the start is seconds. Common schedules:
0 */30 * * * *— every 30 minutes0 0 8 * * *— every day at 08:00 UTC0 0 3 * * 1— Mondays at 03:00 UTC
Common trap: Azure Timer trigger CRON uses 6 fields, not 5 like standard Unix cron. If you use a 5-field expression, deployment will fail with a schedule-parsing error.
Bindings: Read and Write Without Boilerplate
Bindings let your function interact with other Azure services — Blob Storage, Cosmos DB, Queues — without writing connection logic. You declare them in function.json and they appear as function parameters.
Here is a function that reads a file from Blob Storage and writes a processed copy:
# function.json
{
"scriptFile": "__init__.py",
"bindings": [
{
"name": "inputblob",
"type": "blobTrigger",
"direction": "in",
"path": "input-container/{name}",
"connection": "AzureWebJobsStorage"
},
{
"name": "outputblob",
"type": "blob",
"direction": "out",
"path": "output-container/{name}",
"connection": "AzureWebJobsStorage"
}
]
}
# __init__.py
import azure.functions as func
def main(inputblob: func.InputStream,
outputblob: func.Out[bytes]) -> None:
data = inputblob.read()
processed = data.upper()
outputblob.set(processed)Code language: PHP (php)
What is happening:
inputblobarrives automatically when a file lands ininput-container.outputblob.set()writes the result — no manual Storage SDK code needed.- The
connectionfield references the storage account connection string from your app settings.
Deploy to Azure
Once your function works locally, deploy it to Azure in three commands:
# 1. Create a resource group (one-time)
az group create --name rg-functions-demo --location westeurope
# 2. Create a storage account (required by Functions runtime)
az storage account create \
--name stfunctionsdemo2026 \
--location westeurope \
--resource-group rg-functions-demo \
--sku Standard_LRS
# 3. Create the Function App and deploy
az functionapp create \
--resource-group rg-functions-demo \
--consumption-plan-location westeurope \
--runtime python \
--runtime-version 3.11 \
--functions-version 4 \
--name func-demo-unique-name \
--storage-account stfunctionsdemo2026
func azure functionapp publish func-demo-unique-nameCode language: PHP (php)
For production workflows, use GitHub Actions or Azure DevOps to automate deployment. If you are pursuing Azure certifications, our Azure DevOps certs guide covers the relevant exams.
After publishing, your HTTP trigger is live at the URL shown in the output. If you are building a frontend on top of Azure Functions, consider pairing it with a CDN for faster global delivery — see our guide on Azure CDN.
https://func-demo-unique-name.azurewebsites.net/api/HttpTriggerDemoCode language: JavaScript (javascript)
Cold Starts: What They Are and How to Fix Them
A cold start happens when your function app hasn’t been invoked recently and Azure needs to allocate a new instance. On the Consumption plan, this can add 2–10 seconds of latency — especially for Python, where the runtime and dependencies must be loaded into memory.
Three practical fixes, from simple to advanced:
- Switch to the Premium plan with at least one pre-warmed instance. This adds a fixed monthly cost (~$70/month per instance) but eliminates cold starts for predictable workloads.
- Enable Always On on the App Service plan. Note: Always On is not available on the Consumption plan.
- Add a warmup trigger — a timer function that pings your HTTP endpoints every 5 minutes during business hours. This keeps instances alive but burns a small number of executions.
Tip: If your function is latency-sensitive (API behind a user-facing app), skip the Consumption plan entirely. The Premium plan with Always On pays for itself by removing the “first request is slow” user experience.
Common Mistakes
Forgetting to install the Azure Storage bindings package
If your function uses Blob or Queue bindings and you see ModuleNotFoundError: No module named 'azure.storage', you are missing the extension:
pip install azure-functions azure-storage-blob
Then update requirements.txt and redeploy.
Python version mismatch between local and Azure
Check what Azure expects. In your Function App on the portal, go to Configuration → General Settings → Python version. Match this in your local environment and in functionapp create --runtime-version. A mismatch can cause silent import failures.
Missing AzureWebJobsStorage connection string
The Functions runtime requires a storage account for triggers, logs, and coordination. If you see “Azure Functions runtime is unreachable” in the portal, check that AzureWebJobsStorage is set in your app settings. It is populated automatically when you create the Function App via CLI — but can break if you reconfigure the storage account later.
Cheat Sheet
| Task | Command |
|---|---|
| Init project | func init --python |
| New HTTP trigger | func new --name MyFunc --template "HTTP trigger" |
| New Timer trigger | func new --name MyTimer --template "Timer trigger" |
| Run locally | func start |
| Deploy | func azure functionapp publish <app-name> |
| View logs | func azure functionapp logstream <app-name> |
| Set app setting | az functionapp config appsettings set --name <app> --settings KEY=VALUE |
How much does Azure Functions cost?
Azure Functions uses a pay-per-execution model. On the Consumption plan, you get 1 million executions and 400,000 GB-seconds free per month. After that, you pay $0.20 per million executions plus $0.000016 per GB-second. For predictable workloads, the Premium plan starts at ~$70/month per instance with pre-warmed capacity.
Which Python versions does Azure Functions support?
Azure Functions v4 supports Python 3.9, 3.10, and 3.11 in production. Python 3.12 is available in preview as of mid-2025. Check the current supported versions with az functionapp list-runtimes --query "[?runtime=='python']".
Can I use async/await in Azure Functions Python?
Yes. Define your main function as async def main(req: func.HttpRequest) -> func.HttpResponse and the Python worker will run it on the event loop. This is particularly useful when your function makes HTTP calls to external APIs or queries a database. Avoid blocking calls like time.sleep() — use asyncio.sleep() instead.
How do I set environment variables for my function?
Add them via Azure CLI: az functionapp config appsettings set --name <app-name> --resource-group <rg> --settings "DB_HOST=mydb.server.com" "API_KEY=sk-...". Locally, store them in local.settings.json under the Values key. Never commit local.settings.json with secrets to version control.
What is the difference between Consumption, Premium, and Dedicated plans?
Consumption: Serverless, pay-per-execution, auto-scales to zero. Cold starts are possible. Best for sporadic or low-traffic workloads.
Premium: Pre-warmed instances, no cold starts, VNET integration. Fixed monthly cost per instance. Use when latency matters or you need always-on workers.
Dedicated (App Service): Runs on regular App Service VMs. Full control but you manage scaling manually. Use when you already have underutilized App Service capacity.








Leave a Reply