Azure · 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
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.