Background
Sections
IntroductionFoundations1. Resource Hierarchy2. Resource Manager3. Identity and RBAC4. Regions and Availability5. Naming and TaggingVirtual Machines1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetVirtual Network1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetBlob Storage1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetAzure SQL Database1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetAzure Kubernetes Service1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetAzure Container Registry1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetMicrosoft Entra ID1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetAzure RBAC1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetAzure Functions1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetAPI Management1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetAzure App Configuration1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetAzure Machine Learning1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetAzure Monitor1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetAzure AI Foundry1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and Cheatsheet

4. Getting Started

6 min read

One HTTP-triggered function, stood up three ways, then deleted. This page is deliberately throwaway: hard-coded names, no variables, no remote state, no pipeline. Everything production-shaped lives in Deployment. If a snippet here would embarrass you in a pull request, that's the point.

The same tiny task each time: create a function app and call an HTTP function that returns a greeting.

Start by creating a throwaway resource group, because deleting one resource group is the cleanest teardown Azure offers — and a genuine advantage over AWS, where the equivalent cleanup is manual or CloudFormation-shaped.

Three very different routes that end at exactly the same pair of resources

What you're actually creating

Even the minimal version is three resources plus one optional-but-do-it component. This is worth seeing before you type anything, because "why does my function app need a storage account?" is the first question everyone asks.

Resource Why it exists
Resource group The container and the teardown handle
Storage account The host's required backing store — triggers, timers, leases, keys, package
Function app (Microsoft.Web/sites) The app itself
Application Insights Optional to create, indispensable in practice

On Consumption, the plan is created implicitly; on other plans it's a fourth resource (Microsoft.Web/serverfarms).

1. Azure Portal

Fast to grasp, not repeatable. Portal navigation changes often, so this is described by destination blade rather than exact button chain.

  • From Create a resource, choose Function App, then pick the hosting option — take Consumption (or Flex Consumption) for this exercise.
  • On Basics, set the resource group to a new rg-func-demo, give the app a globally unique name, pick a runtime stack (Node.js or Python is quickest) and a region near you.
  • On Storage, accept the new storage account it offers; on Monitoring, leave Application Insights enabled.
  • Create it, then open the app's Overview blade and wait for the deployment to finish.
  • In the app's Functions blade, create a function from the HTTP trigger template, then use Code + Test to run it and Get function URL to call it from a browser.

Note what the portal quietly did for you: created the storage account, wired AzureWebJobsStorage, created an Application Insights component, and set the runtime version. Every one of those becomes an explicit line in the IaC versions below — which is exactly why portal-created resources are hard to reproduce.

2. Azure CLI

Copy-pasteable and repeatable. Storage account names must be globally unique, lowercase, and alphanumeric; function app names must be globally unique too. Change the two names below before running.

# --- variables (change the two unique names) ---
RG=rg-func-demo
LOC=uksouth
STORAGE=stfuncdemo$RANDOM
APP=func-demo-$RANDOM

# 1. Throwaway resource group — the teardown handle
az group create -n $RG -l $LOC

# 2. The host's required storage account
az storage account create \
  -n $STORAGE -g $RG -l $LOC \
  --sku Standard_LRS \
  --allow-blob-public-access false \
  --min-tls-version TLS1_2

# 3. The function app, on a Consumption plan (created implicitly)
az functionapp create \
  -n $APP -g $RG \
  --storage-account $STORAGE \
  --consumption-plan-location $LOC \
  --runtime node \
  --runtime-version 20 \
  --functions-version 4 \
  --os-type Linux

# 4. Confirm it exists and is running
az functionapp show -n $APP -g $RG --query "{name:name, state:state, kind:kind}" -o table

⚠️ Runtime names and supported versions change — az functionapp list-runtimes prints what's actually available today, and is more reliable than any figure written down here.

Now deploy a function. The quickest honest path is Azure Functions Core Tools, which scaffolds the project structure the runtime expects:

# Scaffold locally
func init demo-fn --javascript
cd demo-fn
func new --name hello --template "HTTP trigger" --authlevel function

# Run it locally first — this uses a local storage emulator or your AzureWebJobsStorage setting
func start

# Publish to the app created above
func azure functionapp publish $APP

Then call it. The URL is printed by publish, but here's how to fetch the key yourself — note that this is a control-plane call returning a data-plane secret, which is the split described in Architecture:

KEY=$(az functionapp function keys list -g $RG -n $APP --function-name hello --query default -o tsv)
curl "https://$APP.azurewebsites.net/api/hello?name=Sunil&code=$KEY"

If you'd rather skip Core Tools entirely, deploy a zip you built yourself:

az functionapp deployment source config-zip -g $RG -n $APP --src ./demo-fn.zip

PowerShell note. Functions apps are usually managed from az or from CI/CD rather than interactively from PowerShell, so the Az equivalents are omitted here — unlike the VM and Entra ID topics, a PowerShell-first shop has no particular advantage on this service.

3. Terraform (minimal)

The smallest thing that works — hard-coded, unparameterised, and not something to ship. The full module shape is in Deployment.

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 4.0"
    }
  }
}

provider "azurerm" {
  features {}
}

resource "azurerm_resource_group" "demo" {
  name     = "rg-func-demo"
  location = "uksouth"
}

resource "azurerm_storage_account" "demo" {
  name                     = "stfuncdemouniquename" # must be globally unique
  resource_group_name      = azurerm_resource_group.demo.name
  location                 = azurerm_resource_group.demo.location
  account_tier             = "Standard"
  account_replication_type = "LRS"
  min_tls_version          = "TLS1_2"
}

# On Consumption, the plan is a real resource with SKU "Y1"
resource "azurerm_service_plan" "demo" {
  name                = "plan-func-demo"
  resource_group_name = azurerm_resource_group.demo.name
  location            = azurerm_resource_group.demo.location
  os_type             = "Linux"
  sku_name            = "Y1" # Y1 = Consumption. EP1-EP3 = Premium. B1/S1/P1v3 = Dedicated
}

resource "azurerm_linux_function_app" "demo" {
  name                = "func-demo-uniquename" # must be globally unique
  resource_group_name = azurerm_resource_group.demo.name
  location            = azurerm_resource_group.demo.location
  service_plan_id     = azurerm_service_plan.demo.id

  storage_account_name       = azurerm_storage_account.demo.name
  storage_account_access_key = azurerm_storage_account.demo.primary_access_key

  site_config {
    application_stack {
      node_version = "20"
    }
  }
}

output "function_app_hostname" {
  value = azurerm_linux_function_app.demo.default_hostname
}
terraform init
terraform plan
terraform apply

Three things to notice, because they're the seeds of the production version:

  • sku_name on the plan is the whole hosting decision. Y1 is Consumption; EP1 is Premium; B1/P1v3 is Dedicated. One string changes cold start, networking, and the bill.
  • The storage access key is in your state file. That's why remote state with restricted access matters, and why the production module in Deployment uses a managed identity for the storage connection instead.
  • Flex Consumption is a different resource type (azurerm_function_app_flex_consumption), not a SKU on azurerm_service_plan. ⚠️ Verify the resource name and schema against the current azurerm provider documentation before using it.

Teardown

Always. Half of all surprise cloud bills come from forgotten demo resources, and a Consumption function app is cheap enough to hide for months.

az group delete -n rg-func-demo --yes --no-wait
# If you used Terraform
terraform destroy

Two caveats worth knowing even at this scale:

  • Deleting the function app does not delete the plan or the storage account. If you deleted resources individually rather than deleting the resource group, check for an orphaned plan — on Premium it bills hourly forever.
  • If you enabled Application Insights, its Log Analytics workspace may live in a different resource group and survive the delete.

Next: Deployment →

← Back to the Azure Functions overview · ← Previous: Architecture