Build a Function with Azure Functions
Learn to build a function with Azure Functions in this step-by-step Azure tutorial. Understand the core concept, follow a hands-on exercise, and troubleshoot common issues.
Focus: build a function with azure functions
You've built the pieces — a database, a storage account, a key vault — but they're just sitting there waiting for code to make them useful. The pain is real: you don't want to stand up a full VM or container just to run a small task every time a file lands in Blob Storage. That's where Azure Functions shines — serverless compute that runs your code in the cloud without you managing servers, scaling, or even thinking about infrastructure. In this lesson, you'll build and deploy your first function end-to-end, from a local runtime to Azure, and see exactly how to make your existing Azure resources respond to events in real time.
The problem this lesson solves
Traditional web apps require you to provision a server (or a cluster) before you can run any code. That's heavy: you pay for idle capacity, patch operating systems, and worry about scaling during traffic spikes. For small, event-driven jobs — resizing an image, sending an email, cleaning up a file, responding to an HTTP request — that's massive overhead for minimal work.
Azure Functions solve this by giving you a function-as-a-service (FaaS) model: you write just the code that matters, the platform runs it on demand, scales it automatically, and you pay only for execution time. The pain this lesson addresses is the gap between having resources and having working automation — you'll close it by shipping a function that connects to a storage account and processes a message.
Pro tip: If you've ever used AWS Lambda or Google Cloud Functions, Azure Functions is the same concept. The
funcCLI and the Azure portal are your main tools, and once you get the trigger model, everything else falls into place.
Core concept / mental model
Think of Azure Functions as event-driven micro-executions. You define a trigger — a condition that fires your code — and a binding — a way to read input or write output without writing boilerplate I/O code. The platform handles the plumbing: it listens for the trigger, spins up a runtime, runs your function, and tears it down when done.
The three pillars: trigger, input, output
- Trigger: What starts your function — an HTTP request, a new blob, a queue message, a timer, etc.
- Input binding: A convenient way to read data before your code runs (e.g., a blob from storage)
- Output binding: A convenient way to write results after your code runs (e.g., a message to a queue)
Everything else — logging, retries, scaling — is handled by the Functions runtime.
A diagram in words
[Blob Container] --> (trigger fires) --> [Azure Function] --> (output binding) --> [Queue]
<-- input binding reads blob data --> <-- writes message -->
In a nutshell: You write a Python function, decorate it with the trigger and binding, and upload it. The platform manages the rest.
How it works step by step
Let's trace the lifecycle of a function from code to execution:
- Create a function app: This is a container for your functions — it defines the runtime, OS, region, and hosting plan (Consumption, Premium, or Dedicated).
- Author a function: Use the CLI (
func new), the portal, or an IDE extension to create a function with a specific trigger. - Define bindings: In the
function.jsonfile (or via decorators in Python), you declare input and output bindings with their parameters. - Deploy: Use
func azure functionapp publishto upload your code to Azure. The CLI packages everything and sends it up. - It just runs: When an event that matches the trigger occurs, Azure spins up a runtime, calls your function, and scales automatically as needed.
Under the hood: the host runtime
Each function app uses a host runtime (e.g., Python 3.10) that loads your function, reads function.json, and wires triggers and bindings. The host is responsible for managing the execution environment — the Python process, the HTTP server, and the storage account that stores the code and metadata.
Hands-on walkthrough
Time to build. We'll create a simple HTTP-triggered function that validates a name and responds with a greeting. Then we'll deploy it to Azure.
Prerequisites
- Azure CLI installed (
az --version) - Azure Functions Core Tools version 4 (
func --version) - Python 3.10+ installed
- An Azure subscription (
az login)
Step 1: Create a function app project
Open a terminal and run:
# Create a folder and go into it
mkdir my-first-function
cd my-first-function
# Initialize a Python function project
func init --python
# Create a new function with an HTTP trigger
func new --name HttpTriggerHello --template "HTTP trigger"
This creates requirements.txt, host.json, local.settings.json, and a function folder named HttpTriggerHello with __init__.py and function.json.
Step 2: Edit the function code
Open HttpTriggerHello/__init__.py and replace its content with:
import azure.functions as func
import logging
app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS)
@app.function_name(name="HttpTriggerHello")
@app.route(route="hello")
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()
except ValueError:
pass
else:
name = req_body.get('name')
if name:
return func.HttpResponse(f"Hello, {name}!")
else:
return func.HttpResponse(
"Please pass a name on the query string or in the request body.",
status_code=400
)
Note: This uses the modern Python v2 programming model (decorators). If you prefer the classic v1 model, the same logic lives in
function.jsonwithscriptFileand bindings — but the v2 model is now the default for new projects.
Step 3: Run it locally
Start the local runtime:
func start
You'll see output like:
Functions:
HttpTriggerHello: http://localhost:7071/api/hello
For detailed output, run func with --verbose flag.
Now test it (in another terminal):
curl "http://localhost:7071/api/hello?name=Alice"
Expected response:
Hello, Alice!
And without a name:
curl "http://localhost:7071/api/hello"
Expected response (with –i to see status code):
HTTP/1.1 400 Bad Request
Please pass a name on the query string or in the request body.
Step 4: Deploy to Azure
Now we'll deploy to the cloud.
# Log in to Azure
az login
# Create a resource group
az group create --name my-functions-rg --location eastus
# Create a storage account (required for Azure Functions)
az storage account create \
--name mystorageaccount123 \
--resource-group my-functions-rg \
--location eastus \
--sku Standard_LRS
# Create a function app (Consumption plan)
az functionapp create \
--name my-function-app-123 \
--resource-group my-functions-rg \
--storage-account mystorageaccount123 \
--consumption-plan-location eastus \
--runtime python \
--runtime-version 3.10 \
--functions-version 4
# Publish your code
func azure functionapp publish my-function-app-123
The publish command will show progress and give you the public URL. Test it:
curl "https://my-function-app-123.azurewebsites.net/api/hello?name=Bob"
You should see Hello, Bob!.
Compare options / when to choose what
Azure Functions are not the only compute option. Here's how they compare to the main alternatives:
| Option | Best for | Scaling | Cost | Management |
|---|---|---|---|---|
| Azure Functions (Consumption) | Event-driven, short-lived jobs (<10 min) | Automatic, instant | Pay per execution | Minimal |
| Azure Functions (Premium) | Long-running, need VNet integration | Automatic, more durable | Pay for baseline + execution | Minimal |
| Azure Functions (Dedicated) | When you need consistent performance | Manual or auto | Pay for VM instances | More involved |
| Azure Container Apps (ACA) | Microservices needing more control | Auto | Pay for compute + scaling | Moderate |
| Virtual Machines | Legacy apps, full control | Manual | Pay for idle | High |
| AKS (Kubernetes) | Containerized microservices at scale | Auto with pods | Pay for nodes | High |
When to choose Functions: You need to respond to events (HTTP, Blob, Queue, Timer) quickly, cost-efficiently, and without managing servers. If you need more than 10 minutes of execution or heavy compute, consider ACA.
Pro tip: Use the Azure Functions Consumption plan for most small to medium workloads. It's the most cost-effective and requires zero capacity planning.
Variations
- Blob trigger: Use
@app.blob_triggerto run code when a file appears in a Blob container (great for image processing pipelines). - Queue trigger: Use
@app.queue_triggerto process messages from an Azure Storage Queue, enabling reliable async processing. - Timer trigger: Use
@app.schedulewith a CRON expression to run code on a schedule (e.g., nightly cleanup).
Troubleshooting & edge cases
Error 1: ImportError: cannot import name 'FunctionApp'
This usually means you have an older version of azure.functions installed. Upgrade:
pip install --upgrade azure-functions
Error 2: Function doesn't appear in portal after publish
Check the function.json file. A malformed binding can block the host from recognizing the function. func start locally will often show the error — run it before you deploy.
Error 3: 401 Unauthorized on HTTP trigger
If you set http_auth_level=func.AuthLevel.FUNCTION (or ADMIN), the endpoint requires a master key. For anonymous public endpoints, use ANONYMOUS. To get keys: in the portal, go to App keys.
Edge case: cold start latency
In the Consumption plan, a function that hasn't run for a while may take a few seconds to start. To mitigate, keep an always-available plan (Premium) or minimize imports.
Edge case: local.settings.json secrets
Never commit local.settings.json — it contains connection strings. Add it to .gitignore. Use Azure Key Vault or App Settings to store secrets in the cloud.
What you learned & what's next
You've learned to build a function with Azure Functions from scratch: you created a function app, wrote a Python HTTP-triggered function, tested it locally, and deployed it to Azure. You now understand the core model of triggers, bindings, and the hosting plans that make Functions a zero-ops solution for event-driven workloads.
Next step: In the next lesson, you'll connect this function to your Azure Storage account using a Blob trigger — automating tasks like image thumbnail generation when files are uploaded. You'll also learn how to securely access connection strings using Azure Key Vault, tying this lesson into the broader Azure tutorial track.
Key takeaway: Azure Functions is the fastest way to turn a piece of Python code into a cloud service that reacts to events. Master the trigger model, and you can automate anything in Azure.
Practice recap
Try extending your function: add a second route that accepts a blob trigger. Create a Blob container in your storage account, upload a file, and have the function log the file name. Also explore setting the auth level to FUNCTION and generating a key to test 401 handling.
Common mistakes
- Forgetting to upgrade
azure-functionspackage to the latest version, leading toImportErrorwithFunctionAppin v2 model. - Committing
local.settings.jsonwith connection strings — a security risk. Always add it to.gitignore. - Using the wrong auth level for HTTP triggers — setting
FUNCTIONwhen you intendedANONYMOUS, causing 401s. - Not checking
func startoutput forfunction.jsonerrors before deploying, resulting in missing functions in the portal.
Variations
- Classic v1 model: define triggers and bindings in
function.jsonwithscriptFileinstead of decorators — still common in older projects. - Blob trigger: use
@app.blob_trigger(arg_name="myblob", path="mycontainer/{name}")to process file uploads. - Queue trigger: use
@app.queue_trigger(arg_name="msg", queue_name="myqueue")for reliable async processing.
Real-world use cases
- Automate image thumbnail generation as soon as a file is uploaded to Blob Storage — using a Blob trigger.
- Power a REST API for a mobile app's lightweight back‑end, like form submission handling, without managing servers.
- Run nightly batch jobs (e.g., data cleanup, report generation) on a Timer trigger to save costs.
Key takeaways
- Azure Functions is serverless compute: write code, set a trigger, and the platform scales automatically.
- Triggers and bindings are the core: you declare what starts your code and how to pass data — no boilerplate I/O.
- Use the Consumption plan for most small jobs — pay only per execution and no idle cost.
- Test locally with
func startbefore deploying; the host runtime catches errors early. - Always protect secrets: use
local.settings.jsononly for local dev, and Azure Key Vault for production. - The Python v2 model uses decorators, while v1 uses
function.json— pick one and stick with it.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.