Skip to main content
Azure · lesson 7 of 10
Azure intermediate Lesson 7 of 10

Azure Functions Fundamentals

Write an HTTP-triggered function, deploy it with func and az functionapp, invoke it twice, and see the timing gap between cold start and a warm call.

By SkillByExample · Updated

azure azure-functions serverless

An Azure Function is a single unit of code, triggered by something (HTTP, a queue message, a timer) and billed by execution rather than by a server running continuously. The Consumption plan — the free-tier-friendly default — trades that low idle cost for a noticeable pause the first time a function runs after being idle: cold start.

Scaffolding and running locally

func init learn-func --javascript
cd learn-func
func new --template "HTTP trigger" --name hello
$ func init learn-func --javascript
Writing host.json
Writing local.settings.json
Writing .gitignore
$ func new --template "HTTP trigger" --name hello
Select a number for template: HTTP trigger
Function name: hello
The function "hello" was created successfully from the "HTTP trigger" template.
func start
$ func start

Azure Functions Core Tools
Core Tools Version:       4.0.5985
Function Runtime Version: 4.34.1.22669

Functions:

        hello: [GET,POST] http://localhost:7071/api/hello

For detailed output, run func with --verbose flag.
curl "http://localhost:7071/api/hello?name=Reader"
$ curl "http://localhost:7071/api/hello?name=Reader"
Hello, Reader. This HTTP triggered function executed successfully.

Core Tools runs the exact same runtime that executes in Azure — the local test isn’t a simulation, which is why local testing is trustworthy for logic even though it can’t reproduce cold start (there’s no deallocation to wait through on your own machine).

Deploying to a real Function App

az storage account create --resource-group learn-rg --name learnfuncsa482 --sku Standard_LRS

az functionapp create \
  --resource-group learn-rg \
  --consumption-plan-location eastus \
  --runtime node \
  --runtime-version 20 \
  --functions-version 4 \
  --name learn-func-482 \
  --storage-account learnfuncsa482
$ az functionapp create --resource-group learn-rg --consumption-plan-location eastus --runtime node --runtime-version 20 --functions-version 4 --name learn-func-482 --storage-account learnfuncsa482
{
  "defaultHostName": "learn-func-482.azurewebsites.net",
  "kind": "functionapp",
  "name": "learn-func-482",
  "state": "Running",
  "resourceGroup": "learn-rg"
}
func azure functionapp publish learn-func-482
$ func azure functionapp publish learn-func-482
Getting site publishing info...
Uploading package...
Uploading 2.14 KB [##################################################]
Deployment successful.
Remote build succeeded!
Syncing triggers...
Functions in learn-func-482:
    hello - [httpTrigger]
        Invoke url: https://learn-func-482.azurewebsites.net/api/hello?code=aBcD1eFgH2iJkL3mNoPqR4sTuVwXyZ5==

The invoke URL includes a function key (?code=...) by default — HTTP-triggered functions require it unless the function’s auth level is set to anonymous, a detail that’s easy to forget when a deployed function returns 401 where the local one returned 200 without a key.

Cold start vs. warm invocation

time curl -s -o /dev/null -w "%{http_code}\n" "https://learn-func-482.azurewebsites.net/api/hello?code=aBcD1eFgH2iJkL3mNoPqR4sTuVwXyZ5==&name=Reader"
$ time curl -s -o /dev/null -w "%{http_code}\n" "https://learn-func-482.azurewebsites.net/api/hello?code=...&name=Reader"
200

real    0m3.412s
user    0m0.031s
sys     0m0.014s

That’s the first call after deployment — no instance was running, so Azure had to allocate one, load the Node runtime, and load the function before it could respond. Immediately running the same command again:

$ time curl -s -o /dev/null -w "%{http_code}\n" "https://learn-func-482.azurewebsites.net/api/hello?code=...&name=Reader"
200

real    0m0.187s
user    0m0.028s
sys     0m0.012s

3.4 seconds down to 0.19 — same code, same request, the only difference is whether an instance was already warm. Wait roughly 20 minutes with no traffic on the Consumption plan and the next call pays the cold-start cost again.

Reading the logs from a live invocation

az functionapp logs tail --resource-group learn-rg --name learn-func-482
$ az functionapp logs tail --resource-group learn-rg --name learn-func-482
2026-09-12T16:02:11 [Information] Executing 'Functions.hello' (Reason='This function was
programmatically called via the host APIs.', Id=3f8a1c2d-...)
2026-09-12T16:02:11 [Information] Executed 'Functions.hello' (Succeeded, Id=3f8a1c2d-...,
Duration=42ms)

Duration=42ms is the function’s own execution time — it excludes the cold-start allocation overhead entirely, which is why this number alone can look fine while the client-observed latency (measured above with time) tells the real story.

Practice

1. List every function currently deployed to the Function App.
az functionapp function list --resource-group learn-rg --name learn-func-482 --output table
$ az functionapp function list --resource-group learn-rg --name learn-func-482 --output table
Name     ResourceGroup    ConfigHref
-------  ---------------  -------------------------------------------------------------
hello    learn-rg         https://learn-func-482.azurewebsites.net/admin/functions/hello

Useful before redeploying, to confirm a function you removed locally actually disappears from the app rather than lingering from a previous deployment.

2. Retrieve the function key programmatically instead of copying it from the publish output.
az functionapp function keys list --resource-group learn-rg --name learn-func-482 --function-name hello --output table
$ az functionapp function keys list --resource-group learn-rg --name learn-func-482 --function-name hello --output table
Name     Value
-------  ------------------------------------------
default  aBcD1eFgH2iJkL3mNoPqR4sTuVwXyZ5==

Scripting deployments against a CI system needs this — the key printed once by func publish isn’t retrievable by re-running publish; this command is the durable way to fetch it.

3. Move the Function App to the Premium plan to eliminate cold starts.
az functionapp plan create --resource-group learn-rg --name learn-premium-plan --location eastus --sku EP1 --is-linux false
az functionapp update --resource-group learn-rg --name learn-func-482 --plan learn-premium-plan
$ az functionapp update --resource-group learn-rg --name learn-func-482 --plan learn-premium-plan
{
  "name": "learn-func-482",
  "sku": "ElasticPremium"
}

EP1 keeps at least one pre-warmed instance always running, which is what removes cold start — at the cost of a fixed hourly charge whether or not the function is invoked, unlike Consumption’s scale-to-zero.

4. Set an application setting the function code can read as an environment variable.
az functionapp config appsettings set --resource-group learn-rg --name learn-func-482 --settings "GREETING_PREFIX=Hey there,"
$ az functionapp config appsettings set --resource-group learn-rg --name learn-func-482 --settings "GREETING_PREFIX=Hey there,"
[
  {
    "name": "GREETING_PREFIX",
    "value": "Hey there,",
    "slotSetting": false
  }
]

Application settings surface as environment variables inside the function’s process (process.env.GREETING_PREFIX in Node) — the standard way to pass configuration without hardcoding it into deployed code.

5. Delete the Function App and its storage account together.
az functionapp delete --resource-group learn-rg --name learn-func-482
az storage account delete --resource-group learn-rg --name learnfuncsa482 --yes
$ az functionapp delete --resource-group learn-rg --name learn-func-482
$ az storage account delete --resource-group learn-rg --name learnfuncsa482 --yes

Deleting the Function App doesn’t remove its storage account automatically — they’re separate resources linked at creation time, so cleanup means deleting both explicitly, or just az group delete on the whole resource group.

Next: Bicep and ARM templates — a minimal Bicep file, deploying it, and what a failed deployment’s error output actually looks like.

Frequently Asked Questions

Do I need an Azure account to write and test a function, or can I run it locally?
Azure Functions Core Tools (the func CLI) runs functions locally with no Azure account needed, using the same runtime that executes in the cloud, which makes local development and unit testing free. Deploying it so it's reachable from the internet, though, requires a real Function App resource in Azure.
What is cold start and why does it only happen sometimes?
On the Consumption plan, Azure deallocates the underlying compute for a function app when it's had no traffic for a while. The next request has to wait for a new instance to spin up and load the runtime and your code before it can execute — a cold start, typically one to several seconds. Once that instance is warm, requests in quick succession reuse it and skip that wait, until it's idle long enough to be deallocated again.
What's the difference between the Consumption plan and the Premium plan for avoiding cold starts?
Consumption bills per execution and per GB-second but scales to zero, so it's the plan where cold starts happen. Premium keeps a configurable number of instances always warm (and still scales out under load), trading a flat baseline cost for consistently low latency — the standard fix when cold start is a real problem for a specific function.
Why did az functionapp create ask for a storage account?
Azure Functions uses a storage account internally to store the function code, manage trigger state (like queue and timer bookkeeping), and coordinate scaling — it's a required dependency of the Function App resource itself, not something you interact with directly for HTTP-triggered functions.