StackPractices
beginner By Mathias Paulenko

Azure Basics — Core Services for Developers

A practical guide to Microsoft Azure core services for developers: compute, storage, databases, networking, identity, and monitoring with hands-on examples.

Overview

A team I worked with moved a .NET monolith to Azure in a weekend: App Service for the web tier, Azure SQL for the database, Blob Storage for uploads, Managed Identity instead of connection-string passwords. No servers patched, no credentials in config files, and the whole thing ran on the free tier for the first month. That stack (compute, storage, a database, a network boundary, and an identity model) is what this guide covers.

Azure is the second-largest cloud platform and the default choice inside organizations already paying for Microsoft 365, Active Directory, and Visual Studio licenses. If you write .NET, it’s the smoothest cloud to deploy to; if you write anything else, the tooling is still solid. The az CLI, the Python/Node SDKs, and GitHub Actions integration all work without touching a Windows machine. This guide walks the services you will actually use in a typical application, how to choose between them, and the mistakes that cost teams real money in their first quarter.

When to Use Azure

Choose Azure when:

  • Your organization already runs on Microsoft: .NET apps, Office 365, Active Directory, Windows Server licenses you can reuse through Azure Hybrid Benefit
  • You need hybrid cloud: ExpressRoute and Azure Arc connect on-premises datacenters better than any competitor’s equivalent
  • You want integrated CI/CD: Azure DevOps is built in, and GitHub Actions (also Microsoft) has first-class Azure deployment actions
  • You’re building enterprise apps where identity matters: Entra ID is the strongest single-sign-on and conditional-access system in any cloud

If your team is AWS-native or your workloads are Kubernetes-first data pipelines, compare with the AWS basics guide before committing. The services map almost one-to-one, and the decision usually comes down to existing contracts and skills.

Setting Up Your Environment

Create a free account first. You get a $200 credit for 30 days plus 12 months of free service quotas (750 hours of B1s VMs, 5 GB of Blob Storage, 1 million Function executions monthly). Then install the Azure CLI:

# Install (Windows: winget install Microsoft.AzureCLI; macOS: brew install azure-cli)
az login

# Set the subscription you'll work against
az account list --output table
az account set --subscription "My Subscription"

# Everything in Azure lives inside a resource group — create one per environment
az group create --name rg-myapp-dev --location eastus

A resource group is a lifecycle boundary: everything inside it gets deployed, billed, and deleted together. Delete the group and every resource inside disappears. That makes resource groups your best friend for spinning up test environments and tearing them down without orphan costs. Tag them early (env:dev, team:payments) or your cost reports become unanswerable questions. A bootstrap script that provisions the whole dev environment from this section is in the companion resources.

Compute — Picking the Right Service

Azure gives you five ways to run code, and choosing wrong is the most expensive beginner mistake. The ladder from most control to least management looks like this:

Mermaid flowchart LR diagram
ServiceModelUse it forAvoid when
Virtual MachinesIaaSLegacy apps, custom OS config, lift-and-shiftYou want the platform to patch and scale for you
App ServicePaaSWeb apps, REST APIs, backends in .NET/Java/Node/Python/PHPBursty workloads that sit idle (you pay for the plan, not the requests)
Azure FunctionsServerlessEvent handlers, scheduled jobs, webhooks, file processingLong-running work over ~10 min on Consumption, or steady high volume (Premium gets pricey)
Container AppsManaged containersMicroservices, Dapr apps, scale-to-zero containersYou need full Kubernetes API control
AKSManaged KubernetesLarge microservice fleets, complex orchestrationSmall teams — the operational tax is real

Azure Virtual Machines

Full control over the OS. You manage patching, scaling, and health. Reserve VMs for software that can’t run on a platform service.

# Create a VM with Azure CLI
az vm create \
  --resource-group rg-myapp-dev \
  --name myVM \
  --image Ubuntu2204 \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys
VM SeriesUse Case
B-seriesBurstable — accumulates CPU credits when idle; cheap for dev/test
D-seriesGeneral purpose production workloads
F-seriesCompute-optimized, high CPU-to-memory ratio
E-seriesMemory-optimized — caches, in-memory databases

The B-series catches people out: it’s cheap because it banks CPU credits while idle and spends them when busy. A CI agent that builds all day will exhaust credits and throttle to baseline. Check the CPU Credits Remaining metric before blaming the code.

App Service

The workhorse for web workloads: you push code or a container, Azure handles the OS, scaling, TLS, and deployment slots.

# Create a plan and a web app
az appservice plan create --name plan-myapp --resource-group rg-myapp-dev --sku B1 --is-linux
az webapp create \
  --resource-group rg-myapp-dev \
  --plan plan-myapp \
  --name my-webapp-123 \
  --runtime "NODE|18-lts"

Deployment slots are the feature that sells it: deploy to a staging slot, warm it up, then az webapp deployment slot swap to trade it with production. If the swap breaks something, swap back. The previous version is still running. That’s a blue-green deployment built into the platform, no scripting required.

Azure Functions

Event-driven code that bills per execution: the first million executions each month are free on the Consumption plan.

[FunctionName("HttpTrigger")]
public static IActionResult Run(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req)
{
    return new OkObjectResult("Hello from Azure Functions");
}

Two operational realities to know before adopting Functions. First, cold start: on Consumption, a function idle for ~20 minutes takes seconds to wake up. Unacceptable for user-facing latency, fine for background jobs. Second, triggers are the point: a Function that only responds to HTTP is usually better off in App Service; Functions earn their complexity when they react to queues, blobs, timers, or Event Grid.

Storage — Blob, Queue, and Table

A storage account hosts four services. Blob gets the attention, but Queue and Table quietly run half of most Azure architectures.

Blob Storage

Object storage for unstructured data: uploads, images, backups, logs, static assets.

# Create a storage account and container
az storage account create --name mystorage123 --sku Standard_LRS --resource-group rg-myapp-dev
az storage container create --name uploads --account-name mystorage123

# Upload a file
az storage blob upload --container-name uploads --file report.pdf --name report.pdf
TierUse CaseGotcha
HotFrequently accessed dataHighest storage price, cheapest access
CoolData stored ≥ 30 days, accessed rarelyEarly-deletion fee if you pull data out before 30 days
ColdData stored ≥ 90 daysSame early-deletion penalty, longer window
ArchiveCompliance archives, ≥ 180 daysRetrieval takes hours — a rehydration job, not a read

The Archive tier looks free until someone restores a backup from it during an incident and waits three hours for rehydration. For the full breakdown of access patterns, lifecycle rules, and CDN pairing, see the Blob Storage guide.

Queue Storage

A dead-simple message queue for decoupling components: cheaper and simpler than Service Bus, good enough for most “process this later” jobs.

from azure.storage.queue import QueueServiceClient

queue = QueueServiceClient.from_connection_string(conn_str).get_queue_client("tasks")
queue.send_message("process-order-123")
message = queue.receive_message()

receive_message doesn’t delete the message; it hides it for a visibility timeout. Your consumer must call delete_message after processing, or the message reappears and gets processed twice. Design consumers to be idempotent; at-least-once delivery means duplicates are a normal event, not an edge case.

Databases — Azure SQL and Cosmos DB

Azure SQL

Managed SQL Server with backups, patching, and high availability handled for you.

# Create a server and database — keep the admin password out of the command
az sql server create --name myserver \
  --admin-user sqladmin \
  --admin-password "$SQL_ADMIN_PASSWORD"
az sql db create --server myserver --name mydb --service-objective S0

The DTU/S0-style tiers are fine for learning, but check the serverless compute tier for dev databases: it auto-pauses when idle and bills per second of actual use, which turns a dev database that sits idle all night into something almost free.

Cosmos DB

Globally distributed NoSQL with a guaranteed single-digit-millisecond read latency: the service you reach for when Azure SQL’s relational model or regional deployment becomes the bottleneck.

from azure.cosmos import CosmosClient

client = CosmosClient(url, credential=key)
database = client.create_database_if_not_exists("mydb")
container = database.create_container_if_not_exists("users", partition_key="/id")
container.upsert_item({"id": "user-1", "name": "Alice"})

The partition_key choice is the decision that haunts Cosmos DB users: every query that filters on a different property fans out across all partitions (a “cross-partition query”) and multiplies your request-unit bill. Pick a key that matches your most common query filter (/id for user lookups, /tenantId for multi-tenant apps) and design the data model around it, not the other way around. The free tier gives you 1,000 RU/s and 25 GB forever, which is enough to learn on.

Networking — Virtual Networks

A Virtual Network (VNet) isolates your resources into a private address space. Everything Azure deploys into a VNet gets a private IP; nothing is reachable from the internet unless you deliberately expose it.

VNet 10.0.0.0/16
├── Subnet A (public)  10.0.1.0/24 — App Service integration, public inbound
└── Subnet B (private) 10.0.2.0/24 — VMs, Private Endpoints, no inbound

The services that matter most:

  • Private Endpoint / Private Link — puts a PaaS service (SQL, Blob, Key Vault) on a private IP inside your VNet. This is how a database stops being reachable from the public internet entirely, and it’s the single most impactful security move in this guide.
  • VNet Peering — connects two VNets across regions or subscriptions with private traffic.
  • Network Security Groups — the firewall rules attached to subnets; default-deny inbound is the sane baseline.
  • Application Gateway — Layer 7 load balancer with a built-in Web Application Firewall.
  • Azure Firewall — managed network-level protection for outbound traffic.

The pattern that bites everyone: a developer creates an Azure SQL server, ticks “Allow Azure services and resources to access this server” to make their app work, and accidentally leaves the database open to every Azure customer — not just their own resources. Use Private Endpoint instead, or at minimum lock the firewall to your outbound IPs.

Identity — Microsoft Entra ID

Azure Active Directory (renamed Microsoft Entra ID in 2023) handles authentication and authorization. For developers, two concepts matter more than the rest:

Managed Identity gives your app an identity without credentials. An App Service with a system-assigned managed identity can call Key Vault, Storage, or SQL with zero secrets in config: Azure handles the token lifecycle, rotation, and revocation. Enable it once and delete a whole class of “connection string leaked” incidents:

az webapp identity assign --name my-webapp-123 --resource-group rg-myapp-dev

RBAC controls who can do what. The roles are coarse (Owner, Contributor, Reader) plus hundreds of narrower ones. Grant at the resource-group level, prefer narrow roles, and never give a CI/CD service principal Contributor on the whole subscription when Website Contributor on one resource group does the job.

A decoded Entra access token carries your app registration’s identity and roles:

{
  "issuer": "https://login.microsoftonline.com/{tenant}/v2.0",
  "audience": "{client-id}",
  "claims": {
    "roles": ["Reader", "Contributor"]
  }
}

For CI/CD, prefer OIDC federation over stored secrets: GitHub Actions can authenticate to Azure with a federated credential. No AZURE_CREDENTIALS secret expiring in a vault you forgot about.

Key Vault — Managing Secrets

Every key, certificate, and connection string your app needs belongs in Key Vault, referenced by name; never in appsettings.json, environment files, or pipeline variables that anyone can print.

az keyvault create --name kv-myapp-123 --resource-group rg-myapp-dev --location eastus
az keyvault secret set --vault-name kv-myapp-123 --name "DbPassword" --value "$SQL_ADMIN_PASSWORD"

The combination that makes this safe in practice: Managed Identity on the app side, Key Vault on the secret side, and an RBAC assignment (Key Vault Secrets User) connecting them. The app asks Key Vault for DbPassword at startup using its managed identity: no human ever reads the value, and rotation takes a single secret set. Enable soft-delete and purge protection before storing anything real; deleted secrets are recoverable for 90 days, which has saved more than one fat-fingered az keyvault secret delete.

Monitoring — Azure Monitor and Application Insights

Azure Monitor is the platform-wide telemetry layer: metrics, logs, and alerts for every resource. Application Insights is the application-level piece inside it: request tracing, dependency calls, exceptions, and live metrics for your code.

az monitor app-insights component create \
  --app myapp-insights \
  --resource-group rg-myapp-dev \
  --location eastus

The part most teams skip is KQL: the query language for Log Analytics, where every diagnostic log lands. Learning five KQL operators (where, summarize, order by, project, render) turns “the app is slow” into “p95 latency doubled on the /checkout endpoint at 14:03, correlated with a deployment.” Set up action-group alerts on the signals that hurt (HTTP 5xx rate, failed dependencies, quota usage) before launch, not after the first incident.

Common Mistakes

  • Deploying everything to one region. A single-region outage takes your whole app down. At minimum know your DR story, and use availability zones for anything that matters.
  • Leaving service principals with client secrets. Secrets expire and leak; Managed Identity or OIDC federation removes the whole problem.
  • Creating resources with no cost controls. A forgotten VM or an Always-On App Service plan burns money silently. Set a budget alert in Cost Management on day one.
  • Over-provisioning compute. Defaulting to a P-series App Service plan or D-series VM for a dev environment; B-series and serverless tiers exist for a reason. Check Azure Advisor recommendations monthly.
  • “Allow Azure services” on databases. That checkbox opens your SQL server to every Azure tenant, not just your app. Use Private Endpoint.
  • Treating Archive tier as cheap backups. Rehydration takes hours. An Archive-tier “backup” you can’t restore during an incident isn’t a backup.

Troubleshooting

  • az deployment fails with quota exceeded. New subscriptions start with low regional vCPU quotas (often 4–10 cores). Check az vm list-usage --location eastus and file a quota-increase support ticket: they’re free and usually approved within hours.
  • Function is slow on the first request after idle. Consumption-plan cold start. Either accept it for background work, keep the function warm with a timer trigger, or move latency-sensitive functions to a Premium plan.
  • App Service deploys but returns 503 / “application error.” Get the real exception: az webapp log tail or the Log stream blade. Nine times out of ten it’s a missing connection string or app setting that exists locally but was never deployed. Compare with az webapp config appsettings list.
  • Swap to production worked, then everything 404s. Slot-specific settings didn’t move. Mark settings as “deployment slot settings” for anything that must stay pinned to a slot.
  • Cosmos DB bills are higher than expected. You’re running cross-partition queries. Check the Query Metrics in Insights: if Retrieved Document CountReturned Document Count, your partition key doesn’t match your query patterns.
  • DefaultAzureCredential fails locally but works in Azure. Locally it falls back to your az login credentials. Run az login in the terminal your app runs in, and check that your account has the same RBAC role the managed identity has in the cloud.

See Also

Companion code: Azure basics resources — the azure-bootstrap.sh script that provisions the dev environment used throughout this guide.

Frequently Asked Questions

Is Azure free to learn on?

Yes — a new account gets $200 credit for 30 days, 12 months of free quotas on popular services (750 hours of B1s VMs, 5 GB Blob Storage, 250 GB Azure SQL), and an always-free tier that includes 1 million Function executions and 1,000 RU/s of Cosmos DB monthly. Put a budget alert on the subscription anyway; free tiers expire and forgotten resources keep billing.

Should I choose Azure, AWS, or GCP?

Azure fits Microsoft-centric organizations: .NET, Entra ID, hybrid requirements. AWS has the broadest service catalog and largest community. GCP leads on data analytics and managed Kubernetes. For a solo developer, the differences matter less than the free tiers and your own familiarity; for a company, existing contracts and skills decide it. The multi-cloud guide covers running workloads across providers when one cloud isn't the answer.

How do I deploy to Azure from GitHub?

Create an OIDC federated credential on an app registration or managed identity, grant it Website Contributor on the resource group, then use the azure/login and azure/webapps-deploy actions in a GitHub Actions workflow. No stored secrets. GitHub exchanges a token with Azure directly. App Service's Deployment Center can also generate the workflow file for you.

How do I choose between Azure SQL and Cosmos DB?

Azure SQL for relational data with stable schema, complex joins, and ACID transactions: the default for business applications. Cosmos DB for semi-structured data that needs horizontal scale, global low-latency reads, or multi-region writes. If you genuinely need both, SQL for transactional records and Cosmos for session state, catalogs, and user profiles is a common split.

What is the difference between App Service and Azure Functions?

App Service runs your app continuously on a plan you pay for: right for web apps and APIs with steady traffic. Functions runs code per event and bills per execution: right for irregular, event-driven work. A Function that serves a busy HTTP API all day usually costs more and performs worse than the same code on App Service.

How do I keep Azure costs under control?

Three habits cover most of it: set a budget with email alerts in Cost Management before creating anything, tag every resource group with env and team so spend is attributable, and review Azure Advisor's cost recommendations monthly. The top offenders are forgotten VMs, Always-On plans left running in dev, and premium tiers provisioned "temporarily" during a launch.