Azure Interview Questions and Answers

Last updated:

Check out 45 of the most common Azure interview questions, then take an AI-powered practice interview

Azure FunctionsBlob StorageCosmos DBAKSDevOps
45+
Questions
18
Basic
18
Intermediate
9
Advanced
Q1

How is the Azure resource hierarchy organised, and what actually happens when you delete a resource group?

BasicResource Model

Answer

Azure nests scopes in four levels: management group, subscription, resource group, and resource. The tenant root management group sits at the top, subscriptions are the billing and quota boundary, and resource groups are logical containers inside a single subscription. RBAC role assignments and Azure Policy assignments inherit downward, so a Reader granted at the management group is a Reader on every resource underneath, and there is no way to subtract that with a normal role assignment (only deny assignments, which Azure Policy and Blueprints create internally, can override an inherited allow).

A resource group has its own location, and candidates routinely get this wrong: the region of the resource group only decides where the resource group metadata lives, not where the resources run. A resource group in Central India can happily hold a storage account in West Europe. If the metadata region has an outage you may not be able to create or update resources in that group, while the resources themselves keep serving traffic.

Deleting a resource group is a bulk delete of everything inside it, executed with as much parallelism as dependencies allow, and it is not recoverable. The only protections are management locks (CanNotDelete or ReadOnly), which block the delete until the lock is removed, and per-service soft delete such as Key Vault soft delete or Recovery Services vault soft delete. Blob soft delete does not save you here, because the storage account itself is gone. Interviewers ask this to check whether you understand scope inheritance and whether you have ever put locks on production.

# Resource group location only stores metadata, not the resources
az group create --name rg-payments-prod --location centralindia

# Deploy a storage account in a completely different region
az storage account create \
  --name stpaymentsprod001 \
  --resource-group rg-payments-prod \
  --location westeurope --sku Standard_ZRS

# Protect production from an accidental bulk delete
az lock create --name no-delete \
  --lock-type CanNotDelete \
  --resource-group rg-payments-prod

# This now fails until the lock is removed
az group delete --name rg-payments-prod --yes

Key Points

  • Management group, subscription, resource group, resource are the four RBAC and Policy scopes
  • Resource group location stores metadata only, resources can live in any region
  • RBAC inherits downward, only deny assignments can override an inherited allow
  • CanNotDelete locks and per-service soft delete are the only real safety nets
💡 Pro Tip: Put a CanNotDelete lock on every production resource group and on the Key Vault holding your signing keys. It costs nothing and has saved more prod environments than any runbook.
Q2

What is Azure Resource Manager, and what is the difference between Incremental and Complete deployment mode?

BasicARM and Deployments

Answer

Azure Resource Manager is the control plane. Every portal click, every az command, every Terraform apply, and every SDK call ends up as an authenticated REST request to management.azure.com, which authenticates against Microsoft Entra ID, evaluates RBAC and Azure Policy, and forwards the request to the relevant resource provider (Microsoft.Storage, Microsoft.Compute, Microsoft.ContainerService, and so on). Two consequences matter in interviews.

First, resource providers must be registered on the subscription before you can create their resources, and the failure message is the confusing MissingSubscriptionRegistration error, fixed with az provider register. Second, ARM applies subscription-level request throttling, historically in the region of 12,000 reads and 1,200 writes per hour per subscription per region, so a badly written polling loop or a Terraform run with hundreds of data sources can get 429 TooManyRequests and stall a pipeline. Deployment mode applies to template deployments.

Incremental, the default, adds or updates the resources described in the template and leaves anything else in the resource group untouched. Complete mode deletes any resource in the target resource group that is not present in the template, which makes the template the single source of truth but is genuinely dangerous if someone created a resource manually. Note that Incremental mode is not a merge at the property level: a property you omit from a resource definition may be reset to its default, which is why a redeploy sometimes silently drops app settings or firewall rules.

# Register a provider before first use
az provider register --namespace Microsoft.ContainerService --wait
az provider show -n Microsoft.ContainerService --query registrationState -o tsv

# Incremental (default): leaves untracked resources alone
az deployment group create \
  --resource-group rg-app-prod \
  --template-file main.bicep \
  --parameters env=prod

# Complete: deletes anything in the RG not described by the template
az deployment group create \
  --resource-group rg-app-prod \
  --mode Complete \
  --template-file main.bicep

# Always preview first
az deployment group what-if \
  --resource-group rg-app-prod \
  --template-file main.bicep

Key Points

  • ARM is the single control plane at management.azure.com for portal, CLI, SDK and Terraform
  • Unregistered resource providers cause MissingSubscriptionRegistration errors
  • Incremental is the default, Complete deletes resources missing from the template
  • Omitted properties can be reset to defaults even in Incremental mode
Q3

Explain Azure regions, availability zones and region pairs. Which of them actually protect you from what?

BasicAvailability

Answer

A region is a set of datacentres within a latency envelope. An availability zone is a physically separate datacentre inside one region with independent power, cooling, and networking, and zone-enabled regions have at least three of them. A region pair is a Microsoft-defined pairing used for platform-managed replication (geo-redundant storage) and for staggered platform updates, so both halves of a pair are not patched at once.

In India, Central India (Pune) supports availability zones, while West India historically does not, which is exactly the kind of detail interviewers use to see whether you have designed anything real in this geography. Central India and South India form a pair, and West India pairs with South India. The protection each layer offers is different.

Availability zones protect against a single datacentre failure: a power event, a cooling failure, a local network partition. Region pairs protect against a regional outage, but only for services that actually replicate across them, and for most of those the replication is asynchronous, so you accept data loss measured in minutes. Nothing protects you automatically.

A virtual machine placed in zone 1 is zonal, not zone-redundant, if zone 1 goes down that VM is down. To be zone resilient you either deploy instances across zones behind a Standard Load Balancer or you pick a service with a zone-redundant SKU: zone-redundant storage, zone-redundant Application Gateway v2, Azure SQL with zone redundancy enabled, or an AKS node pool spread across zones. Public IPs and load balancers also come in Basic (no zone support, and being retired) and Standard flavours.

# Which zones does a region expose for VMs?
az vm list-skus --location centralindia --size Standard_D4s_v5 \
  --query "[].locationInfo[].zones" -o tsv

# Zonal VM: pinned to one zone, dies if that zone dies
az vm create -g rg-web -n vm-web-1 --zone 1 --image Ubuntu2204 --size Standard_D4s_v5

# Zone-redundant public IP + LB is what makes the front door survive a zone loss
az network public-ip create -g rg-web -n pip-web \
  --sku Standard --zone 1 2 3

# Zone-redundant storage (synchronous across three zones in-region)
az storage account create -g rg-web -n stwebprod001 \
  --location centralindia --sku Standard_ZRS

Key Points

  • Zones protect against a datacentre failure, pairs protect against a regional failure
  • Central India has availability zones, West India does not
  • Zonal (pinned to one zone) is not the same as zone-redundant
  • Basic SKU public IPs and load balancers have no zone support and are being retired
Q4

What are the Azure Blob access tiers, and what are the real costs of using Cool, Cold and Archive?

BasicStorage

Answer

A general-purpose v2 storage account supports four blob access tiers: Hot, Cool, Cold, and Archive. Hot has the highest storage price and the lowest access price. Cool is cheaper to store, more expensive to read, and carries a 30-day minimum retention.

Cold, added more recently, sits between Cool and Archive with a 90-day minimum. Archive is offline: the blob metadata stays queryable but the data itself is not readable at all until you rehydrate it, and the minimum retention is 180 days. Two costs surprise teams.

The first is the early deletion penalty: if you delete or re-tier a Cool blob after 10 days, you are still billed for the full 30 days, and the same arithmetic applies to Cold at 90 days and Archive at 180. A lifecycle rule that aggressively pushes data to Archive and a retention job that deletes it two months later can cost more than leaving everything Hot. The second is rehydration.

Moving a blob out of Archive takes hours at Standard priority and up to about an hour at High priority for smaller blobs, and it is a copy operation you must poll for, not a synchronous read. Any application that assumes it can read an archived blob on demand will throw BlobArchived (HTTP 409). Tiering is per blob for block blobs, and you can also set a default account tier. Interviewers usually follow up by asking how you would automate this, which is the lifecycle management policy.

# Set a tier on a single blob
az storage blob set-tier --account-name stlogs001 \
  --container-name audit --name 2026/01/report.parquet --tier Archive

# Rehydrate: this is an async copy, not a read
az storage blob set-tier --account-name stlogs001 \
  --container-name audit --name 2026/01/report.parquet \
  --tier Hot --rehydrate-priority High

# Poll until the blob is readable again
az storage blob show --account-name stlogs001 \
  --container-name audit --name 2026/01/report.parquet \
  --query "properties.rehydrationStatus" -o tsv

Key Points

  • Hot, Cool (30-day min), Cold (90-day min), Archive (180-day min)
  • Early deletion penalties can make aggressive tiering more expensive, not less
  • Archive blobs are offline, reading one returns HTTP 409 BlobArchived
  • Rehydration is an asynchronous copy you must poll, hours at Standard priority
💡 Pro Tip: Before proposing Archive in a design round, ask how the data is read. If a compliance auditor needs it within an hour, Cold is the correct answer and Archive is a trap.
Q5

What are the ways to authenticate to Azure Blob Storage, and why does an Owner still get 403 AuthorizationPermissionMismatch?

BasicStorage Security

Answer

There are three mechanisms: shared account keys, shared access signatures, and Microsoft Entra ID with Azure RBAC. Account keys are the two 512-bit keys on the storage account, they grant full control over every container and blob, they cannot be scoped, and they are the single most commonly leaked Azure secret in Indian codebases. A shared access signature is a signed query string that grants limited permissions for a limited time.

A service SAS or account SAS is signed with the account key, so a leaked SAS is only revocable by rotating the key or deleting the stored access policy behind it. A user delegation SAS is signed with a key obtained from Entra ID and is the version you should prefer, because it is bound to an identity and expires with the delegation key. The third and best mechanism is Entra ID with data-plane RBAC roles.

Now the interview trap: control-plane and data-plane authorisation are separate systems. Owner and Contributor at the subscription or resource group scope let you manage the storage account, read its keys, and change its firewall, but they grant zero permission to read a blob through the Entra ID path. Attempting it returns HTTP 403 with the error code AuthorizationPermissionMismatch.

You need a data role such as Storage Blob Data Reader, Storage Blob Data Contributor, or Storage Blob Data Owner, assigned at the account, container, or even blob-prefix scope. Role assignment propagation is not instant either, so a freshly granted role can take a few minutes to work.

# Control plane role: can manage the account, cannot read blobs via Entra ID
az role assignment create --assignee $UPN --role "Contributor" \
  --scope /subscriptions/$SUB/resourceGroups/rg-data

# Data plane role: this is the one that fixes AuthorizationPermissionMismatch
az role assignment create --assignee $UPN \
  --role "Storage Blob Data Contributor" \
  --scope /subscriptions/$SUB/resourceGroups/rg-data/providers/Microsoft.Storage/storageAccounts/stdata001/blobServices/default/containers/raw

# User delegation SAS: signed via Entra ID, no account key involved
az storage blob generate-sas --auth-mode login --as-user \
  --account-name stdata001 --container-name raw --name file.csv \
  --permissions r --expiry 2026-08-12T00:00:00Z -o tsv

Key Points

  • Account keys grant unscoped full control and cannot be narrowed
  • User delegation SAS (--auth-mode login --as-user) is preferred over key-signed SAS
  • Control-plane Owner does not imply data-plane blob access
  • Storage Blob Data Reader/Contributor/Owner are the roles that matter
Q6

How do App Service plans and pricing tiers work, and when does Always On actually matter?

BasicApp Service

Answer

An App Service plan is the compute you rent: a set of VM instances of a given size and tier, on which one or many web apps, API apps, and function apps run. You are billed for the plan, not for the apps, so ten low-traffic apps sharing one Standard plan cost the same as one app on it, but they also share CPU, memory, and the 1 GB per-app temporary storage. Tiers run Free and Shared (metered CPU quotas, no custom domain SSL on Free, no Always On), Basic (dedicated VMs, manual scale, no autoscale, no deployment slots), Standard (autoscale, five slots, daily backups), Premium v3 (more memory per core, faster storage, up to 20 slots, zone redundancy, VNet integration) and Isolated v2 (dedicated App Service Environment inside your VNet).

Always On is a per-app setting available from Basic upward that keeps the app warm by pinging it, because App Service unloads an app after roughly 20 minutes without requests. Without Always On, the first request after idle pays a full cold start including runtime boot and JIT, and any in-process background timer, hosted service, or Hangfire scheduler simply stops running when the worker unloads. That is the real reason Always On matters: not latency, but silently dead background jobs.

Candidates who mention only the cold start miss the point interviewers are checking. Note that Always On does not apply to Consumption-plan Functions, which use a different scaling model entirely.

az appservice plan create -g rg-web -n asp-web-prod \
  --sku P1v3 --is-linux --number-of-workers 2 --zone-redundant

az webapp create -g rg-web -p asp-web-prod -n app-orders-prod \
  --runtime "NODE:20-lts"

# Keep the worker loaded so background timers keep firing
az webapp config set -g rg-web -n app-orders-prod --always-on true

# Autoscale is a Standard+ feature and lives on the plan, not the app
az monitor autoscale create -g rg-web --resource asp-web-prod \
  --resource-type Microsoft.Web/serverfarms \
  --name autoscale-web --min-count 2 --max-count 10 --count 2

Key Points

  • You pay for the plan, apps on the same plan share CPU and memory
  • Slots start at Standard, autoscale starts at Standard, VNet integration at Premium v3
  • Without Always On the app unloads after about 20 minutes idle
  • Unloading silently kills in-process background timers and schedulers
Q7

How do Azure VM sizes and managed disk types interact, and why does a Premium SSD sometimes deliver less IOPS than its spec?

BasicCompute and Storage

Answer

Azure VM sizes are grouped into families: B series are burstable and accumulate CPU credits, D series are general purpose, E series are memory optimised, F series are compute optimised, L series are storage optimised with local NVMe, M series are the very large memory SKUs, and N series carry GPUs. Managed disks come as Standard HDD, Standard SSD, Premium SSD, Premium SSD v2, and Ultra Disk. Premium SSD performance is tied to disk size tiers (P10, P30, P40 and so on), while Premium SSD v2 and Ultra let you set capacity, IOPS, and throughput independently, which is usually cheaper for workloads that need high IOPS on a small disk.

The gotcha interviewers love is the double cap. Every VM size has its own uncached and cached disk throughput and IOPS limits, and your effective performance is the minimum of the VM limit and the sum of the disk limits. Attaching four P30 disks that each promise 5,000 IOPS to a VM rated for 6,400 IOPS gives you 6,400, not 20,000.

Host caching adds another layer: ReadOnly caching helps read-heavy database data files, but caching on a write-heavy log disk can hurt, and caching must be None for Ultra Disk and for write-heavy SQL log volumes. Ephemeral OS disks store the OS on the local temp SSD, which makes reimaging fast and removes storage cost, but the disk is lost on deallocate or on any host maintenance that moves the VM, so it suits stateless scale set nodes only.

# Check the VM-level IOPS ceiling before sizing disks
az vm list-skus --location centralindia --size Standard_D8s_v5 --output json \
  --query "[0].capabilities[?name=='UncachedDiskIOPS'].value" -o tsv

# Premium SSD v2: set IOPS and throughput independently of size
az disk create -g rg-db -n disk-sql-data \
  --size-gb 256 --sku PremiumV2_LRS \
  --disk-iops-read-write 12000 --disk-mbps-read-write 500 --zone 1

az vm disk attach -g rg-db --vm-name vm-sql-1 --name disk-sql-data --caching None

# Ephemeral OS disk for stateless scale set nodes
az vmss create -g rg-web -n vmss-web --image Ubuntu2204 \
  --ephemeral-os-disk true --os-disk-caching ReadOnly --instance-count 3

Key Points

  • Effective IOPS is min(VM limit, sum of disk limits), not the disk spec alone
  • Premium SSD tiers are size-linked, Premium SSD v2 and Ultra decouple size from IOPS
  • Host caching must be None for Ultra Disk and for write-heavy log volumes
  • Ephemeral OS disks are free and fast but vanish on deallocate
Q8

How do VNets, subnets and network security groups work, and what surprises people about NSG rules?

BasicNetworking

Answer

A virtual network is a private address space in one region and one subscription, carved into subnets. Azure reserves five IP addresses in every subnet: the network address, the broadcast address, and three used by the platform for the default gateway and DNS mapping, so a /29 gives you three usable addresses, not eight. A network security group is a stateful five-tuple firewall you attach to a subnet, to a NIC, or to both.

Rules are evaluated by priority from 100 upward, first match wins, and every NSG carries default rules with priority 65000 and above: AllowVnetInBound, AllowAzureLoadBalancerInBound, DenyAllInBound, AllowVnetOutBound, AllowInternetOutBound, DenyAllOutBound. Three things trip people up. First, NSGs are stateful, so if you allow an inbound flow the response is automatically permitted, and writing a matching outbound rule is unnecessary.

Second, if an NSG is applied on both the subnet and the NIC, inbound traffic must pass the subnet NSG then the NIC NSG, and outbound the reverse, so the effective rule set is the intersection and debugging gets painful. Third, NSGs match IP addresses, ports, and service tags, not fully qualified domain names, so allowing outbound access to a specific SaaS endpoint by hostname needs Azure Firewall, not an NSG. Service tags such as Storage, AzureActiveDirectory, Sql.CentralIndia and AzureMonitor are the maintainable way to express these rules, because Microsoft keeps the underlying prefix list current.

az network vnet create -g rg-net -n vnet-prod \
  --address-prefix 10.20.0.0/16 \
  --subnet-name snet-app --subnet-prefix 10.20.1.0/24

az network nsg create -g rg-net -n nsg-app

# Service tags beat hard-coded IP ranges
az network nsg rule create -g rg-net --nsg-name nsg-app \
  -n allow-sql-out --priority 200 --direction Outbound --access Allow \
  --protocol Tcp --destination-address-prefixes Sql.CentralIndia \
  --destination-port-ranges 1433

az network vnet subnet update -g rg-net --vnet-name vnet-prod \
  -n snet-app --network-security-group nsg-app

# When a flow is blocked, ask Azure which rule did it
az network watcher test-ip-flow -g rg-app --vm vm-app-1 \
  --direction Outbound --protocol TCP --local 10.20.1.4:12345 \
  --remote 10.20.2.4:1433

Key Points

  • Azure reserves five IPs per subnet, so plan prefixes with that in mind
  • Rules evaluate by priority, first match wins, defaults start at 65000
  • NSGs are stateful, no matching return rule is needed
  • NSGs cannot filter by FQDN, use Azure Firewall for that
Q9

What is the difference between Microsoft Entra ID, Azure RBAC and a subscription?

BasicIdentity

Answer

Microsoft Entra ID (the identity service formerly named Azure Active Directory) is the tenant-level directory: users, groups, service principals, app registrations, managed identities, and the policies that govern how they sign in, including multi-factor authentication and Conditional Access. It answers who you are. Azure RBAC is the authorisation system on Azure resources, built from role definitions (a set of allowed Actions and DataActions plus NotActions) bound to a principal at a scope.

It answers what you may do. A subscription is a billing and quota container that trusts exactly one Entra ID tenant, and a tenant can have many subscriptions. Candidates who treat Entra roles and Azure roles as the same thing get caught immediately, because they are separate systems with separate portals.

Global Administrator is an Entra directory role, it manages users, applications, and directory settings, and by default it grants no access at all to Azure resources. The elevation path exists, a Global Administrator can toggle access management for Azure resources and thereby gain User Access Administrator at the root scope, but it is a deliberate action that shows in the audit log. Conversely, Owner on a subscription lets you do anything to resources in that subscription but does not let you create a user or reset a password. In interviews, the follow-up is almost always about least privilege: prefer built-in roles over custom ones, assign to Entra groups rather than to individuals, scope as narrowly as the workload allows, and use Privileged Identity Management for just-in-time elevation of Owner and User Access Administrator.

# Entra directory role: manages identities, not Azure resources
az rest --method get --url "https://graph.microsoft.com/v1.0/me/memberOf"

# Azure RBAC: what a principal may do to resources
az role assignment list --assignee alice@contoso.com --all -o table

# Assign to a group, not a person, and scope it tightly
GROUP_ID=$(az ad group show -g "payments-oncall" --query id -o tsv)
az role assignment create --assignee-object-id $GROUP_ID \
  --assignee-principal-type Group \
  --role "Reader" \
  --scope /subscriptions/$SUB/resourceGroups/rg-payments-prod

# What can this role actually do?
az role definition list --name "Storage Blob Data Reader" \
  --query "[0].permissions[0]" -o json

Key Points

  • Entra ID handles authentication, Azure RBAC handles resource authorisation
  • Global Administrator is a directory role with no Azure resource access by default
  • One subscription trusts exactly one tenant, a tenant may hold many subscriptions
  • Assign roles to groups at the narrowest workable scope, use PIM for elevation
Q10

What are managed identities, and how does DefaultAzureCredential resolve one at runtime?

BasicIdentity

Answer

A managed identity is a service principal in Entra ID that Azure creates and whose credentials Azure rotates for you, so your code never holds a secret. There are two kinds. A system-assigned identity is created with the resource, shares its lifecycle, and dies when the resource is deleted, which suits a single app that needs its own identity.

A user-assigned identity is a standalone resource you can attach to many compute resources, which is what you want when a scale set, a set of function apps, and an AKS workload all need the same permissions, or when you must create the role assignments before the compute exists (a common chicken-and-egg problem in Bicep deployments). At runtime, the Azure platform exposes a local token endpoint to the compute resource, and DefaultAzureCredential from the Azure Identity SDK walks an ordered chain of credential types until one produces a token: environment variables, then workload identity, then managed identity, then developer tooling such as the Azure CLI, Azure PowerShell, and Azure Developer CLI. That is precisely why the same code works on your laptop under az login and in production under a managed identity with no code change.

The production gotchas are worth naming. If a resource has more than one user-assigned identity attached, the platform cannot guess which one you mean, so you must pass the client ID explicitly. Newly created role assignments take a few minutes to propagate, and the token itself is cached for roughly an hour, so a permission granted after the app started may not take effect until the cached token expires. The chain also adds latency on a cold start, so in production pin the credential type rather than relying on discovery.

// Node.js: same code on a laptop and on an Azure VM / App Service / AKS pod
import { DefaultAzureCredential, ManagedIdentityCredential } from '@azure/identity';
import { BlobServiceClient } from '@azure/storage-blob';

// Dev + prod, discovery chain, slower cold start
const cred = new DefaultAzureCredential({
  managedIdentityClientId: process.env.AZURE_CLIENT_ID, // required if >1 UAMI
});

// Production: pin it, skip the chain
const prodCred = new ManagedIdentityCredential({
  clientId: process.env.AZURE_CLIENT_ID,
});

const blobs = new BlobServiceClient(
  'https://stdata001.blob.core.windows.net',
  prodCred,
);

const container = blobs.getContainerClient('raw');
for await (const b of container.listBlobsFlat()) console.log(b.name);

Key Points

  • System-assigned dies with the resource, user-assigned is reusable and pre-creatable
  • DefaultAzureCredential tries env vars, workload identity, managed identity, then dev CLI
  • With multiple user-assigned identities you must pass the client ID
  • Tokens cache for about an hour, so new role assignments are not instant
💡 Pro Tip: If a managed identity works locally but 401s in production, print the token's oid claim and compare it to the object ID in your role assignment. Nine times out of ten the app picked up a different identity.
Q11

How does Azure Key Vault store secrets, and what do soft delete and purge protection change?

BasicKey Vault

Answer

Key Vault holds three object types with different semantics: secrets (arbitrary strings such as connection strings and API keys), keys (asymmetric or symmetric keys where the private material never leaves the vault and you call the vault to sign, verify, wrap, or unwrap), and certificates (an X.509 certificate plus its managed lifecycle, which can auto-renew against an integrated issuer). Every object is versioned, and a URI without a version always resolves to the current version, which is what makes rotation transparent to callers that reference the versionless URI. Access control comes in two flavours.

Legacy vault access policies are a per-vault list of principals and allowed operations. The modern and recommended model is Azure RBAC on the vault, with roles like Key Vault Secrets User (read secret values), Key Vault Secrets Officer (manage secrets), and Key Vault Crypto User. RBAC wins because it inherits from higher scopes, works with Privileged Identity Management, and is auditable the same way as any other Azure role.

Soft delete is now always on and cannot be disabled: a deleted secret, key, certificate, or even the vault itself moves to a recoverable state for a retention period of 7 to 90 days, and the name stays reserved during that window. That produces the classic pipeline failure where recreating a vault with the same name fails with ConflictError VaultAlreadyExists until you run az keyvault recover or purge it. Purge protection goes further, it blocks purging entirely until retention expires, which is required by many compliance baselines and is irreversible once enabled.

az keyvault create -g rg-sec -n kv-payments-prod \
  --enable-rbac-authorization true \
  --enable-purge-protection true \
  --retention-days 90

az role assignment create --assignee $APP_PRINCIPAL_ID \
  --role "Key Vault Secrets User" \
  --scope $(az keyvault show -n kv-payments-prod --query id -o tsv)

az keyvault secret set --vault-name kv-payments-prod \
  -n razorpay-webhook-secret --value "$SECRET"

# Versionless URI so rotation needs no redeploy
az keyvault secret show --vault-name kv-payments-prod \
  -n razorpay-webhook-secret --query id -o tsv

# A deleted vault name stays reserved until recovered or purged
az keyvault list-deleted -o table
az keyvault recover -n kv-payments-prod

Key Points

  • Secrets, keys and certificates are versioned, versionless URIs make rotation invisible
  • Prefer RBAC authorization over legacy access policies
  • Soft delete is mandatory, deleted vault names stay reserved for 7 to 90 days
  • Purge protection is irreversible and blocks early purge entirely
Q12

What tooling do you use to drive Azure, and how do you query CLI output with --query and JMESPath?

BasicTooling

Answer

Four tools cover almost everything. Azure CLI (az) is cross-platform, Python-based, and the default for shell scripting and CI. Azure PowerShell (the Az module) is preferred in Windows-heavy shops and reads better when you need to pipe objects.

Bicep is the domain-specific language for declarative Azure infrastructure that transpiles to ARM JSON, and it is the right answer when the question is Azure-only. Terraform with the azurerm provider is the right answer when you need one workflow across Azure plus other providers, which is common in Indian services firms managing multi-cloud estates. Azure Developer CLI (azd) wraps app scaffolding, provisioning, and deployment for a whole project.

What separates fluent CLI users is --query, which takes a JMESPath expression and reshapes the JSON before it prints. Combined with -o tsv it turns az into something you can pipe into shell variables without installing jq. Learn the four constructs that cover most needs: property projection with [].name, filters with [?tags.env=='prod'], multiselect hashes with {n:name, l:location}, and functions like contains(), starts_with(), sort_by(), and length().

Two operational habits matter too. Set the subscription explicitly with az account set at the top of every script, because a script that silently runs against the wrong subscription is the fastest way to break someone else's environment. And use az config set core.output=json in automation while keeping table output for humans.

az account set --subscription "prod-payments"

# Project just what you need
az vm list --query "[].{name:name, size:hardwareProfile.vmSize, rg:resourceGroup}" -o table

# Filter server-side in JMESPath
az resource list --query "[?tags.env=='prod' && type=='Microsoft.Storage/storageAccounts'].name" -o tsv

# Find every storage account still allowing public blob access
az storage account list \
  --query "[?allowBlobPublicAccess].{name:name, rg:resourceGroup}" -o table

# Capture a value into a shell variable, no jq needed
KV_ID=$(az keyvault show -n kv-payments-prod --query id -o tsv)

# Orphaned disks: attached to nothing, still billed
az disk list --query "[?diskState=='Unattached'].{n:name, gb:diskSizeGb}" -o table

Key Points

  • az for scripting, Az PowerShell for Windows shops, Bicep for Azure-only IaC, Terraform for multi-cloud
  • --query takes JMESPath, -o tsv makes results shell-friendly
  • Filters, projections, and multiselect hashes cover most real queries
  • Always pin the subscription with az account set inside scripts
Q13

How do Azure Functions triggers and bindings work, and how do the hosting plans differ?

BasicAzure Functions

Answer

A function has exactly one trigger, which decides when it runs and supplies the payload, plus any number of input and output bindings, which are declarative connections to other services so you do not write SDK plumbing. Common triggers are HTTP, Timer (a CRON expression with six fields where the first is seconds, a frequent mistake for people used to five-field CRON), Blob, Queue, Service Bus, Event Hub, Event Grid, and Cosmos DB change feed. Bindings can be an input (read a Cosmos document by id before the function body runs) or an output (write a row to a table or post to a queue by returning a value).

Hosting plans are what interviewers actually probe. Consumption bills per execution and per GB-second, scales to zero, and therefore has cold starts and a default 5-minute timeout that can be raised to 10. Flex Consumption, the newer serverless plan, keeps per-execution billing but adds always-ready instances, configurable per-instance concurrency, and proper virtual network integration, which removes the two historical reasons teams abandoned Consumption.

Premium plan keeps instances pre-warmed, supports VNet integration and unbounded duration, and costs whether or not you get traffic. Dedicated App Service plan runs functions on an existing plan, useful when you already pay for idle capacity. Container Apps hosting is the option when the function must ship as a container alongside other microservices. Note that scale-out in Consumption is driven by an external scale controller watching queue depth or event lag, not by CPU, which is why a slow downstream dependency causes runaway parallel invocations unless you cap host concurrency.

// Node.js v4 programming model: trigger + output binding
const { app, output } = require('@azure/functions');

const queueOut = output.storageQueue({
  queueName: 'invoice-jobs',
  connection: 'AzureWebJobsStorage',
});

app.http('createInvoice', {
  methods: ['POST'],
  authLevel: 'function',
  extraOutputs: [queueOut],
  handler: async (request, context) => {
    const body = await request.json();
    context.extraOutputs.set(queueOut, { invoiceId: body.id });
    return { status: 202, jsonBody: { queued: body.id } };
  },
});

// Six-field CRON: seconds first. This is every 5 minutes, not every 5 seconds.
app.timer('reconcile', { schedule: '0 */5 * * * *', handler: reconcile });

Key Points

  • One trigger per function, plus any number of input and output bindings
  • Timer triggers use six-field CRON with seconds in position one
  • Consumption scales to zero with cold starts, Flex Consumption adds always-ready instances and VNet support
  • Scaling is event-driven via the scale controller, not CPU-driven
💡 Pro Tip: Set maxConcurrentCalls or batchSize in host.json before load testing a queue-triggered function. Default concurrency plus a fragile downstream API is how teams DDoS their own database.
Q14

Compare Azure SQL Database, SQL Managed Instance and SQL Server on a VM. When do you pick each?

BasicDatabases

Answer

These are three points on a control-versus-responsibility curve. Azure SQL Database is a single database or elastic pool as a fully managed PaaS service. Microsoft owns patching, backups, high availability, and the underlying OS, and you get automatic tuning, point-in-time restore, and built-in threat detection.

What you give up is instance scope: no SQL Agent, no cross-database queries in the classic sense, no CLR, no Database Mail, and no linked servers. Azure SQL Managed Instance closes almost all of that gap by giving you a near-complete SQL Server instance inside your virtual network, including SQL Agent, cross-database transactions, Service Broker, and native backup and restore, which makes it the standard lift-and-shift target for legacy applications that Indian services teams migrate from on-premises data centres. It costs more and it deploys into a delegated subnet, so network planning is part of the design.

SQL Server on an Azure virtual machine is pure IaaS: you own the OS, patching, backups, and the always-on availability group, and you pick it only when you need something the PaaS tiers cannot do, such as an unsupported version, a third-party agent that requires OS access, or full control of the file system. Purchasing models add another axis. DTU is a blended bundle of CPU, memory, and IO expressed as a single number, simple but opaque. vCore exposes cores and memory separately, is the only model that supports Hyperscale and serverless, and is required for Azure Hybrid Benefit, so most new production workloads pick vCore.

# PaaS single database, vCore, General Purpose, zone redundant
az sql db create -g rg-db -s sqlsrv-prod -n orders \
  --edition GeneralPurpose --family Gen5 --capacity 4 \
  --zone-redundant true --backup-storage-redundancy Zone

# Serverless: auto-pause for dev and spiky workloads
az sql db create -g rg-db -s sqlsrv-dev -n orders-dev \
  --edition GeneralPurpose --compute-model Serverless \
  --family Gen5 --capacity 4 --auto-pause-delay 60

# Hyperscale when the database outgrows 4 TB
az sql db update -g rg-db -s sqlsrv-prod -n orders \
  --edition Hyperscale --family Gen5 --capacity 8 --read-replicas 1

# Restore to a point in time (PaaS gives you this for free)
az sql db restore -g rg-db -s sqlsrv-prod -n orders \
  --dest-name orders-restored --time "2026-08-10T09:15:00Z"

Key Points

  • SQL Database is PaaS without instance-scope features like SQL Agent
  • Managed Instance is the lift-and-shift target, deploys into a delegated subnet
  • SQL on VM only when you need OS-level control
  • vCore is required for Hyperscale, serverless and Azure Hybrid Benefit
Q15

What are the core concepts in Azure Cosmos DB: request units, partition keys and consistency levels?

BasicCosmos DB

Answer

Cosmos DB is a globally distributed, multi-model database where throughput is expressed in request units per second. A request unit is a normalised cost covering CPU, IOPS, and memory, and the canonical anchor is that reading a 1 KB document by id and partition key costs 1 RU. Writes cost roughly 5 RU or more depending on indexing, and a cross-partition query can cost hundreds.

Throughput is provisioned per container or shared at the database level, either as a fixed value or as autoscale, which floats between 10 percent and 100 percent of a maximum you set and bills for the peak RU/s used each hour. Exceed your provisioned rate and Cosmos returns HTTP 429 with an x-ms-retry-after-ms header rather than queueing your request. The partition key is the single most consequential design decision.

Cosmos hashes the key to place documents into logical partitions, each capped at 20 GB, which are mapped onto physical partitions that each hold up to 50 GB and 10,000 RU/s. Provisioned throughput is divided evenly across physical partitions, so a key with poor cardinality creates a hot partition that gets throttled while most of your capacity sits idle. Consistency has five levels: Strong, Bounded Staleness, Session, Consistent Prefix, and Eventual.

Session is the default and is the right answer for most applications, it guarantees read-your-own-writes within a client session using a session token. Strong is only available with restrictions in multi-region write configurations and costs roughly double the RU for reads. Weaker levels cost fewer RU and give lower latency.

// Point read: 1 RU for a 1 KB doc, always cheaper than a query
const { resource, headers } = await container
  .item('order-8891', 'tenant-42')   // id + partition key
  .read();
console.log('RU charge', headers['x-ms-request-charge']);

// Same data via a query: parsed, planned, and more expensive
const { resources } = await container.items
  .query({
    query: 'SELECT * FROM c WHERE c.id = @id',
    parameters: [{ name: '@id', value: 'order-8891' }],
  }, { partitionKey: 'tenant-42' })   // omit this and it fans out
  .fetchAll();

// Autoscale container: floats between 400 and 4000 RU/s
await database.containers.createIfNotExists({
  id: 'orders',
  partitionKey: { paths: ['/tenantId'] },
}, { maxThroughput: 4000 });

Key Points

  • 1 RU is a 1 KB point read, writes cost about 5 RU or more
  • Logical partition cap is 20 GB, physical partition cap is 50 GB and 10,000 RU/s
  • Throughput splits evenly across physical partitions, so hot keys throttle early
  • Session consistency is the default and correct choice for most apps
💡 Pro Tip: Log x-ms-request-charge on every Cosmos call in non-production. Teams that do this catch accidental cross-partition fan-outs weeks before the bill does.
Q16

Azure Load Balancer, Application Gateway, Front Door and Traffic Manager all distribute traffic. How do you choose?

BasicNetworking

Answer

They operate at different layers and different scopes, and mixing them up is one of the fastest ways to fail an Azure design round. Azure Load Balancer is layer 4, regional, and works on TCP and UDP. It cannot read a URL, cannot terminate TLS, and cannot rewrite headers, but it is extremely fast and it is what sits in front of AKS services of type LoadBalancer and behind most internal architectures.

Application Gateway is layer 7 and regional. It terminates TLS, routes on hostname and URL path, rewrites headers and URLs, supports session affinity, autoscaling, and connection draining, and it hosts Azure Web Application Firewall with the managed OWASP core rule sets. Azure Front Door is layer 7 and global: it is an anycast edge network that terminates TLS close to the user, applies WAF at the edge, caches static content, and routes to the healthiest origin across regions using latency-based and priority-based rules.

Traffic Manager is DNS-based global routing, it returns a different answer per query using routing methods such as Performance, Priority, Weighted, and Geographic, and because it works only in DNS it is the right choice for endpoints that are not HTTP, but it inherits DNS caching, so client failover is bounded by the TTL rather than by the health probe interval. The common production shape for an Indian consumer product is Front Door at the edge for WAF, TLS, and multi-region failover, Application Gateway or an ingress controller inside each region, and Load Balancer under the cluster.

# Global edge: WAF, caching, latency routing, instant failover
az afd profile create -g rg-edge --profile-name afd-prod --sku Premium_AzureFrontDoor
az afd origin-group create -g rg-edge --profile-name afd-prod \
  --origin-group-name og-api --probe-path /healthz --probe-protocol Https \
  --probe-interval-in-seconds 30 --sample-size 4 \
  --successful-samples-required 3 --additional-latency-in-milliseconds 50

# Regional layer 7 with WAF and path routing
az network application-gateway create -g rg-net -n agw-prod \
  --sku WAF_v2 --min-capacity 2 --max-capacity 10 \
  --vnet-name vnet-prod --subnet snet-agw --public-ip-address pip-agw

# Regional layer 4, no URL awareness at all
az network lb create -g rg-net -n lb-internal --sku Standard \
  --vnet-name vnet-prod --subnet snet-app --frontend-ip-name feip

Key Points

  • Load Balancer is L4 regional, Application Gateway is L7 regional
  • Front Door is L7 global anycast with edge WAF and caching
  • Traffic Manager is DNS only, so failover speed is bounded by TTL
  • Typical stack: Front Door, then Application Gateway or ingress, then Load Balancer
Q17

How do Azure Monitor, Log Analytics and Application Insights fit together, and how do you write a basic KQL query?

BasicObservability

Answer

Azure Monitor is the umbrella platform with two data types: metrics, which are pre-aggregated numeric time series stored in a fast time-series store with a fixed retention, and logs, which are schema-on-write records stored in a Log Analytics workspace and queried with Kusto Query Language. Log Analytics is that workspace plus the query engine. Application Insights is the application performance monitoring product, and since the workspace-based model it stores its data in a Log Analytics workspace too, which is why you can join application traces to platform diagnostics in one query.

Data reaches the workspace through diagnostic settings on each resource (this is the step people forget, resource logs are not collected by default and you pay only for what you enable), through the Azure Monitor Agent driven by data collection rules on virtual machines, and through the Application Insights SDK or the Azure Monitor OpenTelemetry distribution in your application. The tables you will be asked about are AzureDiagnostics and resource-specific tables for platform logs, Heartbeat and Perf for machines, and requests, dependencies, exceptions, traces, and customEvents for Application Insights. KQL reads top to bottom as a pipeline: pick a table, filter with where, shape with project or extend, aggregate with summarize, and order with top or sort. Two operators do most of the heavy lifting in interviews: summarize with bin() for time bucketing, and the percentile aggregations for latency, because p95 and p99 are the numbers an SRE cares about, not the average.

// p95 latency and failure rate per API route, last 24 hours
requests
| where timestamp > ago(24h)
| summarize
    calls = count(),
    failures = countif(success == false),
    p50 = percentile(duration, 50),
    p95 = percentile(duration, 95),
    p99 = percentile(duration, 99)
  by name, bin(timestamp, 1h)
| extend failureRate = round(100.0 * failures / calls, 2)
| where calls > 100
| order by p95 desc

// Which dependency is dragging the slow requests?
dependencies
| where timestamp > ago(1h) and success == false
| summarize failed = count() by target, type, resultCode
| top 10 by failed desc

Key Points

  • Metrics are pre-aggregated and fast, logs are KQL over a Log Analytics workspace
  • Resource logs are not collected until you create a diagnostic setting
  • Application Insights tables: requests, dependencies, exceptions, traces, customEvents
  • summarize with bin() and percentile() answers most latency questions
Q18

What levers actually reduce an Azure bill, and how do reservations, savings plans and spot differ?

BasicCost Management

Answer

Start with visibility. Cost Management gives you cost analysis grouped by subscription, resource group, tag, and service, plus budgets with action groups that fire alerts at percentage thresholds. Tagging is the prerequisite: without a mandatory tag policy for owner, environment, and cost centre, chargeback in a large Indian enterprise estate becomes guesswork.

Then the commitment levers. A reserved instance is a one-year or three-year commitment to a specific VM series in a specific region, giving up to roughly 70 percent off pay-as-you-go, and it applies automatically to any matching running instance. Reservations exist for more than VMs: Azure SQL vCores, Cosmos DB throughput, App Service Isolated stamps, and storage capacity all have reserved options.

A savings plan for compute is a commitment to an hourly spend amount rather than to a specific SKU, so it flexes across VM families, App Service, Container Apps, and Functions Premium at a slightly smaller discount. The rule of thumb is reservations for stable, unchanging workloads and savings plans when your instance mix keeps changing. Spot VMs give very deep discounts on surplus capacity but can be evicted with 30 seconds notice, so they belong in batch processing, CI agents, and stateless AKS node pools with tolerations, never in a stateful tier.

Azure Hybrid Benefit reuses existing Windows Server and SQL Server licences with Software Assurance and is frequently the single biggest saving in a migration from on-premises, which is exactly the scenario most Indian services engagements are working on. Finally, hunt waste: unattached managed disks, orphaned public IPs, idle App Service plans, and over-provisioned Log Analytics retention.

# Budget with an alert at 80 percent of monthly spend
az consumption budget create --budget-name bud-prod-monthly \
  --amount 500000 --category Cost --time-grain Monthly \
  --start-date 2026-08-01 --end-date 2027-08-01

# Waste hunt: disks nobody attached, still billed every hour
az disk list --query "[?diskState=='Unattached'].{name:name, rg:resourceGroup, gb:diskSizeGb}" -o table

# Orphaned public IPs
az network public-ip list \
  --query "[?ipConfiguration==null].{name:name, rg:resourceGroup, sku:sku.name}" -o table

# Enforce tagging so chargeback is possible at all
az policy assignment create --name require-costcentre \
  --policy "871b6d14-10aa-478d-b590-94f262ecfa99" \
  --params '{"tagName":{"value":"costCentre"}}' \
  --scope /subscriptions/$SUB

Key Points

  • Reservations lock a SKU and region, savings plans lock an hourly spend and stay flexible
  • Spot is for batch, CI and stateless node pools only, eviction notice is 30 seconds
  • Azure Hybrid Benefit is usually the largest saving in a Windows or SQL migration
  • Unattached disks, orphaned public IPs and idle plans are the standard waste list
Q19

How do you structure a Bicep deployment with modules, and what does az deployment group what-if actually tell you?

IntermediateInfrastructure as Code

Answer

Bicep is a transpiler over ARM JSON, so everything it produces is still an ARM template, but the authoring experience is far better: implicit dependency inference from symbolic references, loops with for, conditionals with if, type-safe parameters with @allowed and @minLength decorators, and modules for composition. A module is just another Bicep file consumed with the module keyword, and behind the scenes it becomes a nested deployment, which is why module outputs are the only supported way to pass values back to the parent. The idiomatic layout is a main.bicep that reads parameters per environment, a modules folder with one file per logical component (network, storage, app, monitoring), and a bicepparam file per environment instead of the older JSON parameter files.

In 2026 the strong recommendation is to consume Azure Verified Modules rather than hand-writing every resource, because they carry the security defaults and diagnostic settings you would otherwise forget. The what-if operation is the Azure-native plan step: ARM runs the deployment in preview and returns a coloured diff classifying every change as Create, Delete, Modify, Deploy, NoChange, or Ignore. The critical nuance is that what-if is a best-effort prediction, not a guarantee.

Properties that the resource provider computes at deploy time, and any resource whose provider does not implement full what-if support, show up as Ignore or as noisy false Modify entries. Deployment stacks address the other half of the problem: a normal deployment never deletes anything you removed from the template, whereas a stack tracks the managed resource set and can detach or delete resources that fall out of scope, with deny settings that also stop humans editing them out-of-band.

// main.bicep
param env string
param location string = resourceGroup().location

@allowed(['Standard_LRS', 'Standard_ZRS'])
param storageSku string = 'Standard_ZRS'

module storage 'modules/storage.bicep' = {
  name: 'storage-${env}'
  params: { name: 'stapp${env}001', sku: storageSku, location: location }
}

module app 'modules/app.bicep' = {
  name: 'app-${env}'
  params: {
    location: location
    // implicit dependsOn: Bicep infers it from this reference
    storageAccountId: storage.outputs.accountId
  }
}

output appUrl string = app.outputs.defaultHostName

Key Points

  • Modules become nested deployments, outputs are the only way to return values
  • Symbolic references create implicit dependsOn, hand-written dependsOn is usually a smell
  • what-if is a best-effort prediction, provider-computed properties show as noise
  • Deployment stacks track a managed resource set and can delete resources removed from the template
💡 Pro Tip: Run what-if as a required pull request check and post the diff as a PR comment. Reviewers approving Bicep they cannot mentally compile is how Complete-mode accidents happen.
Q20

How do private endpoints work, and why does a private endpoint resolve to a public IP from an on-premises network?

IntermediatePrivate Networking

Answer

A private endpoint is a network interface with a private IP from your subnet, mapped through Azure Private Link to a specific sub-resource of a PaaS service, for example the blob sub-resource of one storage account or the sqlServer sub-resource of one Azure SQL logical server. Unlike a service endpoint, which keeps the public IP and simply routes traffic over the Azure backbone while restricting the service firewall to your subnet, a private endpoint gives the service a genuinely private address that is reachable from peered virtual networks, from ExpressRoute, and from site-to-site VPN. The part that generates most production incidents is DNS.

The service hostname does not change, you still connect to stdata001.blob.core.windows.net, and public DNS resolves that name to a CNAME pointing at stdata001.privatelink.blob.core.windows.net, which in turn resolves to the public IP unless something overrides it. When you create the private endpoint with private DNS integration, Azure adds an A record in a private DNS zone named privatelink.blob.core.windows.net linked to your virtual network, so resources inside that VNet get the private IP. Anything resolving through a different DNS server does not: an on-premises machine over ExpressRoute, a peered VNet with custom DNS servers, or a VNet that was never linked to the zone will all get the public address and then fail, because the storage firewall denies public traffic.

The fix is a DNS forwarding design, typically a DNS Private Resolver or a pair of forwarder VMs in the hub, with conditional forwarders on the on-premises DNS pointing privatelink zones at Azure. Remember also that NSGs did not originally apply to private endpoint NICs, so do not rely on that as your only control.

az network private-endpoint create -g rg-net -n pe-stdata-blob \
  --vnet-name vnet-prod --subnet snet-pe \
  --private-connection-resource-id $(az storage account show -n stdata001 --query id -o tsv) \
  --group-id blob --connection-name c-blob

az network private-dns zone create -g rg-net -n privatelink.blob.core.windows.net
az network private-dns link vnet create -g rg-net -n link-prod \
  --zone-name privatelink.blob.core.windows.net --virtual-network vnet-prod --registration-enabled false

az network private-endpoint dns-zone-group create -g rg-net \
  --endpoint-name pe-stdata-blob -n zg1 \
  --private-dns-zone privatelink.blob.core.windows.net --zone-name blob

# Lock the front door: no public data-plane traffic at all
az storage account update -n stdata001 --public-network-access Disabled

# Verify from a peered VNet or on-prem, this must return a 10.x address
nslookup stdata001.blob.core.windows.net

Key Points

  • Private endpoint is a NIC in your subnet mapped to one sub-resource (blob, sqlServer, vault)
  • Service endpoint keeps the public IP, private endpoint gives a real private IP
  • The hostname never changes, only DNS resolution does, via privatelink zones
  • On-premises and custom-DNS VNets need conditional forwarders or a DNS Private Resolver
Q21

Explain LRS, ZRS, GRS, RA-GRS and GZRS. What is the recovery point objective of each?

IntermediateStorage Durability

Answer

Locally redundant storage keeps three synchronous copies inside a single datacentre, so it survives disk and rack failure but not the loss of that facility. Zone-redundant storage keeps three synchronous copies across three availability zones in the region, so a zone outage is invisible to your application and the recovery point objective is effectively zero. Geo-redundant storage is LRS in the primary region plus asynchronous replication to the paired secondary region, where another three LRS copies are kept.

Geo-zone-redundant storage is the combination that most production workloads should use: ZRS in the primary plus asynchronous geo-replication to the secondary. Read-access variants, RA-GRS and RA-GZRS, additionally expose a read-only secondary endpoint at accountname-secondary.blob.core.windows.net, which is the only way to actually read the replica before a failover. The number that matters in interviews is the recovery point objective.

Synchronous options (LRS and ZRS) have an RPO of zero. Geo-replication is asynchronous with an RPO typically under 15 minutes, and there is no contractual guarantee that the last writes made it, so a customer-initiated account failover can lose data. The Last Sync Time property on the account tells you how far behind the secondary is, and you should read it before triggering a failover.

Two operational gotchas: after an unplanned account failover, the account is downgraded to LRS in the new primary and you must explicitly re-enable geo-redundancy, and the failover applies to the whole storage account rather than to a single container. Also note that Archive tier blobs are not supported in ZRS, GZRS or RA-GZRS accounts.

# How far behind is the geo-replica right now?
az storage account show -n stdata001 -g rg-data \
  --expand geoReplicationStats \
  --query "geoReplicationStats.{status:status, lastSync:lastSyncTime, canFailover:canFailover}"

# Read from the secondary without failing over (RA-GRS / RA-GZRS only)
az storage blob download --account-name stdata001 \
  --container-name raw --name file.csv --file ./file.csv \
  --blob-endpoint https://stdata001-secondary.blob.core.windows.net

# Customer-initiated failover: whole account, and it downgrades to LRS
az storage account failover -n stdata001 -g rg-data --yes
az storage account update -n stdata001 -g rg-data --sku Standard_GZRS

Key Points

  • LRS and ZRS are synchronous, RPO zero, ZRS survives a zone loss
  • GRS and GZRS replicate asynchronously, RPO usually under 15 minutes with no guarantee
  • Only the RA- variants let you read the secondary before a failover
  • Unplanned failover is account-wide and leaves the account on LRS afterwards
Q22

How do you choose a Cosmos DB partition key, and how do you fix an existing hot partition?

IntermediateCosmos DB

Answer

A good partition key has three properties: high cardinality so data spreads across many logical partitions, even access distribution so no single value takes a disproportionate share of requests, and alignment with your most frequent query filter so reads stay single-partition. Those goals conflict. Using id gives perfect spread but every query that is not a point read becomes a cross-partition fan-out.

Using tenantId aligns beautifully with queries in a multi-tenant SaaS and then falls over the day one enterprise customer generates ten times the traffic of everyone else, because that tenant is one logical partition, capped at 20 GB, sitting on one physical partition capped at 10,000 RU/s. Date-based keys are the classic anti-pattern for write-heavy telemetry, since every write in the current hour hammers a single partition while the rest of the container idles. Diagnosing a hot partition is done with the Azure Monitor metrics Normalized RU Consumption, split by PartitionKeyRangeId: if one range sits near 100 percent while the container average is 20 percent, you have found it.

The remedies, in order of preference: use hierarchical partition keys (up to three levels, for example tenantId then userId then month) so that Cosmos can split a large tenant across physical partitions while still routing a tenant-scoped query efficiently; or add a synthetic key that concatenates a natural key with a bucket suffix; or, for genuinely enormous tenants, isolate them in their own container. There is no way to change a partition key in place, so any fix means creating a new container and migrating data, typically via the change feed or Azure Data Factory, which is exactly why interviewers weight this question so heavily.

// Hierarchical partition key: tenant stays queryable, big tenants can still split
await database.containers.createIfNotExists({
  id: 'events',
  partitionKey: { paths: ['/tenantId', '/userId'], kind: 'MultiHash', version: 2 },
});

// Single-partition read using the full hierarchy
await container.items.query(
  'SELECT * FROM c WHERE c.type = "login"',
  { partitionKey: ['tenant-42', 'user-9001'] },
).fetchAll();

// Prefix query: still efficient, targets only that tenant's partitions
await container.items.query(
  'SELECT TOP 50 * FROM c ORDER BY c.ts DESC',
  { partitionKey: ['tenant-42'] },
).fetchAll();

// Synthetic key alternative when hierarchy does not fit
const bucket = hash(doc.userId) % 20;
doc.pk = `${doc.tenantId}-${bucket}`;

Key Points

  • Need cardinality, even distribution, and alignment with the common query filter
  • Date keys are the classic write hot spot, tenantId is the classic skew hot spot
  • Diagnose with Normalized RU Consumption split by PartitionKeyRangeId
  • Partition keys are immutable, fixing one means a container migration via change feed
Q23

What causes Azure Functions cold starts, and how do Durable Functions handle long-running orchestration?

IntermediateAzure Functions

Answer

A cold start is the time to allocate a worker, mount your deployment package, boot the language runtime, load dependencies, and run any startup code before the first invocation executes. On the Consumption plan the platform deallocates idle instances, so the next request pays that cost, and the size of it depends heavily on the stack: a small Node or Python app is typically well under a second, while a .NET in-process app with a large dependency graph, or any app that resolves a full DI container and opens database pools at startup, can take several seconds. The practical levers are: run from a package so the file system is a mounted zip rather than thousands of unpacked files, trim dependencies aggressively, avoid heavy work in the constructor or startup path, use the .NET isolated worker with ReadyToRun compilation, and if the workload genuinely cannot tolerate the delay, move to Flex Consumption with always-ready instances or to a Premium plan with pre-warmed instances.

Durable Functions solves a different problem: functions are stateless and time-bounded, but real workflows need state across minutes or days. Durable adds an orchestrator function that is replayed deterministically from an event-sourced history stored in the task hub, so it can await activity functions, wait for external events, and sleep for days without holding compute. The rules follow from replay: orchestrator code must be deterministic, so no DateTime.Now, no random numbers, no direct IO and no unordered awaits, use the context APIs instead. The canonical patterns are function chaining, fan-out fan-in, async HTTP with a status endpoint, monitor loops, and human interaction with a timeout.

// Durable fan-out / fan-in, JavaScript v4 model
const df = require('durable-functions');

df.app.orchestration('reconcileAll', function* (context) {
  // deterministic: use context APIs, never Date.now() or Math.random()
  const tenants = yield context.df.callActivity('listTenants');

  const tasks = tenants.map((t) => context.df.callActivity('reconcileTenant', t));
  const results = yield context.df.Task.all(tasks);   // fan-in

  const failed = results.filter((r) => !r.ok);
  if (failed.length) {
    yield context.df.callActivity('raiseAlert', failed);
  }

  // sleep for a day without burning compute
  const next = new Date(context.df.currentUtcDateTime.getTime() + 86400000);
  yield context.df.createTimer(next);
  context.df.continueAsNew(null);   // avoid unbounded history growth
});

Key Points

  • Cold start = worker allocation + package mount + runtime boot + your startup code
  • Run from package, trim dependencies, keep startup work minimal
  • Flex Consumption always-ready or Premium pre-warmed instances remove it at a cost
  • Orchestrators replay, so they must be deterministic and use continueAsNew for eternal loops
Q24

Compare kubenet, Azure CNI and Azure CNI Overlay for AKS. Why does a cluster run out of IP addresses?

IntermediateAKS

Answer

The networking plugin decides where pod IP addresses come from, and that decision is effectively permanent for the life of the cluster. With classic Azure CNI, every pod gets a real IP from the node subnet. Routing is flat and pods are directly reachable from anywhere in the virtual network, which is excellent for integration with on-premises systems, but the IP maths is brutal: Azure pre-allocates maxPods addresses per node, default 30 on CNI, so a 100-node cluster consumes roughly 3,000 addresses before a single pod schedules, and a /24 node subnet supports about eight nodes.

This is the number one cause of the SubnetIsFull error and of a cluster autoscaler that silently stops adding nodes. Kubenet gives pods addresses from a separate logical CIDR and uses user-defined routes on the node subnet to reach them, which conserves VNet address space but caps you at the route table limit, breaks some Windows and network policy scenarios, and is now on a deprecation path. Azure CNI Overlay is the modern default: nodes get VNet IPs, pods get addresses from a private overlay CIDR that is not part of the VNet, and traffic leaving the cluster is NAT-ed to the node IP.

You keep CNI performance and Azure network policy support while consuming only one VNet address per node, and the same overlay CIDR can be reused across clusters. The trade-off is that pods are not directly addressable from outside the cluster, so anything that dials a pod IP from on-premises needs a service and a load balancer instead. There is also Azure CNI with dynamic IP allocation, which puts pods on their own subnet and allocates in blocks, a middle ground when you do need routable pod IPs.

# Overlay: one VNet IP per node, pods live in a non-routable CIDR
az aks create -g rg-aks -n aks-prod \
  --network-plugin azure --network-plugin-mode overlay \
  --pod-cidr 192.168.0.0/16 \
  --vnet-subnet-id $NODE_SUBNET_ID \
  --network-policy azure --max-pods 110

# Classic CNI IP maths before you commit to a subnet size
# usable = (nodes + surge nodes) * (max-pods + 1)
# 100 nodes * 31 = 3100 addresses, so /20 minimum, not /24

# Diagnose exhaustion
az network vnet subnet show -g rg-net --vnet-name vnet-prod -n snet-aks \
  --query "{prefix:addressPrefix, ips:ipConfigurations | length(@)}"
kubectl get events -A --field-selector reason=FailedScheduling

Key Points

  • Classic CNI pre-allocates maxPods IPs per node from the VNet subnet
  • Kubenet saves addresses but is route-table limited and being deprecated
  • CNI Overlay uses one VNet IP per node and a reusable private pod CIDR
  • Network plugin cannot be changed after cluster creation in most cases
💡 Pro Tip: Size the AKS node subnet for the cluster you will have in two years, including upgrade surge nodes. Resizing a subnet with a live cluster in it is not a maintenance-window job.
Q25

How does AKS workload identity work, and why did it replace pod-managed identity?

IntermediateAKS Security

Answer

Workload identity federation lets a Kubernetes service account exchange its projected token for a Microsoft Entra ID access token without any secret stored in the cluster. The cluster runs an OIDC issuer, you register that issuer URL as a federated identity credential on an Entra application or user-assigned managed identity, and you constrain the trust to a specific namespace and service account name. At runtime the mutating webhook injects the projected service account token, the AZURE_CLIENT_ID, AZURE_TENANT_ID, and AZURE_FEDERATED_TOKEN_FILE environment variables into any pod whose service account carries the right annotation, and the Azure Identity SDK exchanges the token transparently, so DefaultAzureCredential just works.

It replaced the older pod-managed identity (aad-pod-identity), which has been retired, for solid reasons: the old model intercepted the instance metadata endpoint with a node-level daemon set, which meant a race between pod start and identity binding that produced intermittent 400 errors on the first calls, it required node-level components with elevated privileges, and it did not work on Windows node pools or on virtual nodes. Federation is a pure token exchange with no node component in the request path. The failure modes to know: the pod label azure.workload.identity/use must be set to true or the webhook does not inject anything; the federated credential subject must match system:serviceaccount:NAMESPACE:NAME exactly, and a mismatch surfaces as AADSTS70021 no matching federated identity record found; the OIDC issuer URL changes if you recreate the cluster; and the identity still needs an ordinary Azure RBAC assignment, since federation only handles authentication.

az aks update -g rg-aks -n aks-prod --enable-oidc-issuer --enable-workload-identity
ISSUER=$(az aks show -g rg-aks -n aks-prod --query oidcIssuerProfile.issuerUrl -o tsv)

az identity create -g rg-aks -n uami-orders
CLIENT_ID=$(az identity show -g rg-aks -n uami-orders --query clientId -o tsv)

az identity federated-credential create -n fic-orders \
  -g rg-aks --identity-name uami-orders \
  --issuer $ISSUER \
  --subject system:serviceaccount:orders:sa-orders \
  --audience api://AzureADTokenExchange

Key Points

  • Kubernetes service account token is federated to Entra ID, no secret in the cluster
  • Subject must be exactly system:serviceaccount:NAMESPACE:NAME
  • Pod needs the azure.workload.identity/use: "true" label for injection to happen
  • Federation is authentication only, you still need an Azure RBAC role assignment
Q26

How do you structure an Azure DevOps YAML pipeline, and how does GitHub Actions authenticate to Azure without a secret?

IntermediateCI/CD

Answer

An Azure Pipelines YAML file is a hierarchy of stages, jobs, and steps. Stages are the deployment boundaries and run sequentially by default, jobs inside a stage run in parallel on separate agents, and steps run sequentially in one agent workspace. Build artifacts move between stages through publish and download, since separate jobs never share a file system.

Deployment jobs are the important construct: a job of type deployment targets an environment, which is where approvals, business hours checks, and required template checks live, and it gives you deployment strategies such as runOnce, rolling, and canary with preDeploy, deploy, routeTraffic, and postRouteTraffic hooks. Variable groups link to Key Vault so secrets are fetched at run time rather than stored in the pipeline, and secret variables are not automatically exposed as environment variables to scripts, which is the cause of the classic empty-variable bug. For authentication, the modern answer on both platforms is workload identity federation rather than a service principal secret.

In Azure DevOps you create an Azure Resource Manager service connection of type workload identity federation, which trusts the organisation's issuer. In GitHub Actions you create a federated credential on an Entra app scoped to a repository, branch, or environment, grant the azure/login action id-token: write permission, and no client secret exists to leak or expire. That last point matters in practice, because expired service principal secrets are one of the most common causes of a Monday morning deployment failure in Indian enterprise pipelines.

# .github/workflows/deploy.yml
permissions:
  id-token: write      # required for OIDC
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4
      - uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      - run: |
          az deployment group what-if -g rg-app-prod -f infra/main.bicep
          az deployment group create -g rg-app-prod -f infra/main.bicep

# Create the trust once, no client secret anywhere
# az ad app federated-credential create --id $APP_ID --parameters '{
#   "name":"gh-main",
#   "issuer":"https://token.actions.githubusercontent.com",
#   "subject":"repo:org/repo:ref:refs/heads/main",
#   "audiences":["api://AzureADTokenExchange"]}'

Key Points

  • Stages are deployment boundaries, jobs are parallel, steps share one workspace
  • Artifacts must be published and downloaded to cross a job boundary
  • Environments hold approvals and checks, deployment jobs unlock rolling and canary strategies
  • Workload identity federation removes service principal secrets entirely
Q27

Service Bus, Event Hubs, Event Grid and Storage Queues all move messages. Which do you pick and why?

IntermediateMessaging

Answer

Storage Queues are the simplest option: a durable queue inside a storage account, at-least-once delivery, up to 64 KB per message, no ordering guarantee, no topics, no sessions, no dead-letter queue of its own. Use them for cheap, simple, high-volume decoupling where you would otherwise write your own polling table. Service Bus is the enterprise message broker.

It supports queues and publish-subscribe topics with SQL-like subscription filters, FIFO within a session, duplicate detection over a time window, scheduled messages, transactions across entities, a real dead-letter queue, and peek-lock semantics with lock renewal. Reach for it when a message represents a command or a business transaction that must not be lost or processed twice, which is why payment and order pipelines in Indian fintech land on it. Event Hubs is a partitioned streaming ingestion service in the Kafka lineage, built for millions of events per second, with consumers reading by offset and checkpointing their position, retention measured in hours or days, and Capture writing raw batches to blob or Data Lake automatically.

Use it for telemetry, clickstream, IoT, and anything you will later replay or feed into stream processing. Event Grid is the reactive eventing backbone: a publish-subscribe service for discrete event notifications with push delivery over HTTP webhooks, filtering on event type and subject, automatic retry with exponential backoff, and a dead-letter destination in blob storage. It is what fires when a blob is created or a resource changes, and it is a poor choice for high-throughput ordered streams. The interview shorthand: commands go to Service Bus, telemetry streams go to Event Hubs, discrete notifications go to Event Grid, and trivial work items go to Storage Queues.

// Service Bus topic with a filtered subscription (commands)
const { ServiceBusClient } = require('@azure/service-bus');
const sb = new ServiceBusClient(fqdn, new DefaultAzureCredential());

const sender = sb.createSender('order-events');
await sender.sendMessages({
  body: { orderId: 'ORD-8891', amount: 4999 },
  subject: 'order.paid',
  sessionId: 'tenant-42',            // FIFO within this session
  messageId: 'ORD-8891-paid',        // enables duplicate detection
});

// Event Hubs (telemetry stream, partitioned)
const { EventHubProducerClient } = require('@azure/event-hubs');
const producer = new EventHubProducerClient(ehNs, 'clickstream', cred);
const batch = await producer.createBatch({ partitionKey: 'tenant-42' });
batch.tryAdd({ body: { page: '/checkout', ts: Date.now() } });
await producer.sendBatch(batch);

Key Points

  • Service Bus for commands: sessions, dedup, transactions, dead-letter queue
  • Event Hubs for streams: partitions, offsets, checkpoints, Capture to blob
  • Event Grid for discrete notifications with push delivery and retry
  • Storage Queues for cheap simple work items with no ordering needs
Q28

Explain Service Bus peek-lock, lock duration, maxDeliveryCount and the dead-letter queue.

IntermediateMessaging

Answer

Service Bus offers two receive modes. ReceiveAndDelete removes the message the moment it is handed to you, so a crash loses it, and it suits only idempotent low-value work. PeekLock, the default, hands you the message and places an exclusive lock on it for the entity's lock duration, maximum five minutes.

You then complete it (removing it), abandon it (releasing the lock so it is redelivered immediately and the delivery count increments), dead-letter it explicitly, or defer it. If your handler neither completes nor abandons before the lock expires, the broker assumes you died and redelivers, which is where the most painful production bug in this space comes from: a handler that takes six minutes to process a message on a five-minute lock will process the same message repeatedly, forever, each attempt racing the last. The fixes are to renew the lock periodically (the modern SDK processor does automatic lock renewal up to a configured maximum), to shorten the work by offloading it, or to raise the lock duration to the five-minute ceiling.

Every queue and subscription has a delivery count and a maxDeliveryCount, default 10. When a message is abandoned or its lock expires that many times, Service Bus moves it to the dead-letter sub-queue at path queueName/$DeadLetterQueue, stamping DeadLetterReason and DeadLetterErrorDescription. Messages also dead-letter on TTL expiry if enabled, on subscription filter evaluation errors, and on session-related failures. The operational rule interviewers want to hear is that a dead-letter queue with no alert on its message count is the same as no dead-letter queue at all, because nobody discovers the poison messages until a customer does.

const { ServiceBusClient } = require('@azure/service-bus');
const sb = new ServiceBusClient(fqdn, cred);

const processor = sb.createReceiver('orders', {
  receiveMode: 'peekLock',
});

processor.subscribe({
  async processMessage(msg) {
    if (msg.deliveryCount > 3) {
      await processor.deadLetterMessage(msg, {
        deadLetterReason: 'TooManyRetries',
        deadLetterErrorDescription: `failed ${msg.deliveryCount} times`,
      });
      return;
    }
    await handle(msg.body);
    await processor.completeMessage(msg);   // never forget this
  },
  async processError(err) { console.error(err); },
}, { maxConcurrentCalls: 8, autoCompleteMessages: false });

// Drain the poison queue
const dlq = sb.createReceiver('orders', { subQueueType: 'deadLetter' });

Key Points

  • PeekLock locks for up to five minutes, expiry means redelivery not failure
  • Handlers slower than the lock duration cause infinite reprocessing
  • maxDeliveryCount defaults to 10, then the message goes to $DeadLetterQueue
  • Alert on active message count in the DLQ or poison messages stay invisible
💡 Pro Tip: Set autoCompleteMessages to false and complete explicitly. Auto-complete plus a swallowed exception is how messages silently disappear from a payment pipeline.
Q29

How do Key Vault references and Azure App Configuration work together for configuration and feature flags?

IntermediateConfiguration

Answer

Key Vault holds secrets, App Configuration holds everything else. App Configuration is a managed key-value store with labels (typically used as the environment dimension), point-in-time snapshots, change notification through Event Grid, and a first-class feature flag schema with filters for percentage rollout, time windows, and targeting specific users or groups. The two integrate: a value in App Configuration can be a Key Vault reference, so your app pulls all configuration from one place while secret material stays in the vault and is resolved with the app's own managed identity, meaning App Configuration never sees the secret.

App Service and Functions offer the same idea directly through an app setting written as @Microsoft.KeyVault(SecretUri=...), which the platform resolves at startup and periodically refreshes. Two gotchas dominate real incidents here. First, the app's managed identity needs the Key Vault Secrets User role and the App Configuration Data Reader role, and if the reference fails to resolve, App Service surfaces the literal @Microsoft.KeyVault(...) string as the setting value rather than failing loudly, so the application starts and then blows up on a connection string that looks like a template.

Second, configuration refresh is not automatic in the SDK unless you register a sentinel key and configure the refresh interval, otherwise a change in the portal does nothing until the process restarts. For feature flags the standard pattern is a sentinel key that changes with every deployment of config, a refresh interval of 30 seconds, and code that reads the flag per request rather than caching it at startup.

# App Service: resolve a secret at startup via managed identity
az webapp config appsettings set -g rg-web -n app-orders-prod --settings \
  "DbPassword=@Microsoft.KeyVault(SecretUri=https://kv-payments-prod.vault.azure.net/secrets/db-password/)"

# App Configuration with an environment label
az appconfig kv set -n appcfg-prod --key Api:Timeout --label prod --value 30
az appconfig kv set -n appcfg-prod --key Sentinel --label prod --value v42

# Feature flag with a 10 percent rollout
az appconfig feature set -n appcfg-prod --feature new-checkout --label prod
az appconfig feature filter add -n appcfg-prod --feature new-checkout --label prod \
  --filter-name Microsoft.Percentage --filter-parameters Value=10

# Verify the reference actually resolved (not the literal template string)
az webapp config appsettings list -g rg-web -n app-orders-prod \
  --query "[?name=='DbPassword'].value" -o tsv

Key Points

  • App Configuration for settings and flags, Key Vault for secret material
  • Key Vault references resolve with the app's managed identity, needs Secrets User role
  • A failed reference shows up as the literal @Microsoft.KeyVault string, not an error
  • Use a sentinel key plus a refresh interval or config changes need a restart
Q30

What is the difference between Azure SQL active geo-replication, auto-failover groups and elastic pools?

IntermediateDatabases

Answer

Active geo-replication creates up to four readable secondary databases in the same or different regions, replicating asynchronously from the primary. Failover is manual and per database, and the secondary keeps its own server name and connection string, so your application has to know both endpoints and switch itself. That makes it a good fit for read scale-out and for controlled migrations, and a poor fit for automated disaster recovery.

An auto-failover group wraps one or more databases on a logical server with two DNS listener endpoints, a read-write listener and a read-only listener, plus an optional automatic failover policy with a grace period. Because the listener CNAME repoints during failover, applications keep the same connection string and simply reconnect, which is what you want for real DR. The constraints matter: the partner server must be in a different region, both servers need matching firewall rules and logins (contained users or a synchronised login strategy, otherwise the app authenticates fine on the primary and fails after failover), and Hyperscale has its own rules around named replicas. Elastic pools solve an unrelated problem: they let many databases share a pool of vCores or DTUs so that spiky, mostly idle tenant databases cost far less than provisioning each individually.

That is the standard multi-tenant SaaS pattern, one database per customer inside a pool, with per-database min and max settings to stop one noisy tenant starving the rest. Interviewers often combine the two: an elastic pool can itself be placed in a failover group, and the pool is failed over as a unit.

# Readable secondary in another region (manual failover)
az sql db replica create -g rg-db -s sqlsrv-prod -n orders \
  --partner-server sqlsrv-dr --partner-resource-group rg-db-dr \
  --secondary-type Geo

# Failover group: one connection string survives a region loss
az sql failover-group create -n fog-orders -g rg-db -s sqlsrv-prod \
  --partner-server sqlsrv-dr --partner-resource-group rg-db-dr \
  --failover-policy Automatic --grace-period 1 --add-db orders

# App connects to the listener, never to the server directly
# Server=fog-orders.database.windows.net;Database=orders;...

# Multi-tenant elastic pool with per-database guard rails
az sql elastic-pool create -g rg-db -s sqlsrv-prod -n pool-tenants \
  --edition GeneralPurpose --family Gen5 --capacity 16 \
  --db-min-capacity 0.25 --db-max-capacity 4

az sql failover-group set-primary -n fog-orders -g rg-db-dr -s sqlsrv-dr

Key Points

  • Geo-replication is per database and manual, the app must know both endpoints
  • Failover groups add read-write and read-only DNS listeners so the connection string never changes
  • Logins and firewall rules must exist on both servers or the app fails after failover
  • Elastic pools share capacity across many databases, the classic multi-tenant SaaS shape
Q31

How do custom RBAC roles work, and why can a correct-looking role assignment still be denied?

IntermediateIdentity

Answer

A role definition is a JSON document containing Actions and NotActions for control-plane operations, DataActions and NotDataActions for data-plane operations, and assignableScopes limiting where the role can be used. Evaluation is straightforward in principle: Azure unions every Action from every role assigned to the principal at or above the resource's scope, subtracts NotActions, and allows the operation if the resulting set matches. There is no ordering, and an explicit deny does not exist in ordinary role assignments, so you cannot subtract an inherited permission by assigning a narrower role lower down.

Several things make a seemingly correct assignment fail. Propagation delay is the most common: a fresh assignment can take a few minutes to appear, and the caller's cached token may hold stale group membership for longer, so signing out and back in is a real fix rather than superstition. Second, control plane and data plane are separate, so Contributor cannot read a blob or a Key Vault secret when RBAC authorization is enabled on the vault.

Third, deny assignments created by Azure Blueprints, by managed applications, or by the deny settings on a deployment stack always win over any allow, and they are invisible unless you look for them specifically. Fourth, an Azure Policy with a Deny effect blocks the request at ARM before RBAC even matters, and the error message names the policy assignment. Fifth, resource locks return a completely different error that people misread as a permission problem. The practical debugging order is: check the effective permissions, then list deny assignments, then check policy, then check locks.

// custom-role.json: restart VMs, nothing else
{
  "Name": "VM Restart Operator",
  "Description": "Restart VMs without any other compute permission",
  "Actions": [
    "Microsoft.Compute/virtualMachines/read",
    "Microsoft.Compute/virtualMachines/restart/action",
    "Microsoft.Insights/alertRules/read"
  ],
  "NotActions": [],
  "AssignableScopes": ["/subscriptions/SUBSCRIPTION_ID"]
}

# az role definition create --role-definition custom-role.json

# Debug in this order when access is refused
# az role assignment list --assignee $PRINCIPAL --all --include-inherited -o table
# az role assignment list-deny --scope $SCOPE -o table
# az policy state list --filter "complianceState eq 'NonCompliant'" --top 5
# az lock list --resource-group rg-app -o table

Key Points

  • Roles union allows, NotActions subtract, there is no ordinary explicit deny
  • Assignments and token group claims take minutes to propagate
  • Deny assignments from Blueprints, managed apps and deployment stacks override all allows
  • A Policy Deny blocks at ARM before RBAC is even consulted
Q32

What is Azure Policy, what are its effects, and how does DeployIfNotExists remediate existing resources?

IntermediateGovernance

Answer

Azure Policy evaluates resources against declarative rules at create time, at update time, and continuously on a background compliance scan roughly every 24 hours. A policy definition contains an if condition over resource fields and a then effect. The effects worth knowing are Audit (flag non-compliance without blocking), Deny (reject the ARM request outright), Append (add a property to the request), Modify (add, update, or remove tags and certain properties, including on existing resources during remediation), AuditIfNotExists and DeployIfNotExists (evaluate a related resource, for example a diagnostic setting attached to the resource), DenyAction (block a specific operation such as delete), Disabled, and Manual for attestation-based controls.

Definitions are grouped into initiatives, which is how regulatory baselines such as the Microsoft cloud security benchmark and CIS are shipped, and assigned at management group, subscription, or resource group scope with exclusions for specific paths. DeployIfNotExists is the one interviewers dig into because it is the only effect that changes the world. When a resource is created without the related resource, the policy deploys an embedded ARM template to fix it, and for resources that already existed you trigger a remediation task that walks the non-compliant set.

It requires a managed identity on the policy assignment, that identity needs the roles listed in the definition's roleDefinitionIds, and it must be granted at a scope that covers the deployment target. The typical failure is a remediation task stuck at Failed because the assignment identity lacks Contributor or Monitoring Contributor on the target scope. The canonical production use is forcing diagnostic settings onto every resource so that logs actually reach a Log Analytics workspace.

# Assign an initiative with a managed identity so DeployIfNotExists can act
az policy assignment create --name diag-baseline \
  --display-name "Send resource logs to Log Analytics" \
  --policy-set-definition $INITIATIVE_ID \
  --scope /subscriptions/$SUB \
  --mi-system-assigned --location centralindia \
  --params '{"logAnalytics":{"value":"'$WORKSPACE_ID'"}}'

# Grant the assignment identity what the definition demands
PID=$(az policy assignment show -n diag-baseline --scope /subscriptions/$SUB --query identity.principalId -o tsv)
az role assignment create --assignee-object-id $PID --assignee-principal-type ServicePrincipal \
  --role "Monitoring Contributor" --scope /subscriptions/$SUB

# Fix everything that already exists
az policy remediation create --name fix-diag \
  --policy-assignment diag-baseline --resource-discovery-mode ReEvaluateCompliance

az policy state summarize --filter "complianceState eq 'NonCompliant'"

Key Points

  • Effects: Audit, Deny, Append, Modify, AuditIfNotExists, DeployIfNotExists, DenyAction, Manual
  • Policy Deny rejects at ARM, before RBAC and before the resource provider
  • DeployIfNotExists and Modify need a managed identity with the definition's roles
  • Existing resources are only fixed when you create a remediation task
Q33

How does Application Insights sampling work, and how do you correlate a request across services with OpenTelemetry?

IntermediateObservability

Answer

Sampling exists because full-fidelity telemetry from a busy service is expensive to ingest and mostly redundant. Application Insights offers adaptive sampling, which is on by default in the SDK and dynamically adjusts the retained percentage to hit a target items-per-second, fixed-rate sampling where you pick the percentage yourself, and ingestion sampling applied on the service side. All of them are correlation-preserving: the decision is made on the operation id hash, so a request and its dependencies, traces, and exceptions are kept or dropped together, which is what makes a sampled end-to-end transaction still readable.

The consequence people forget is that counts in the portal are estimates: every retained item carries an itemCount, and any KQL you write against sampled data must use sum(itemCount) rather than count() or your numbers will be wrong by the sampling factor. Exceptions and custom metrics can be excluded from sampling if losing them is unacceptable. For correlation, Azure Monitor follows the W3C Trace Context standard, propagating traceparent across HTTP boundaries and message brokers, and the ingestion pipeline maps it onto the operation_Id and operation_ParentId columns.

The current recommendation for new services is the Azure Monitor OpenTelemetry distribution rather than the classic SDK: you get vendor-neutral instrumentation, automatic collection for HTTP, SQL, and the Azure SDKs, and the option to fan out to a second backend later. Manual spans are added through the standard OpenTelemetry API. The one thing to verify in a distributed system is that every hop propagates the header, because a single service that drops traceparent breaks the trace into two disconnected halves.

// Node.js: Azure Monitor OpenTelemetry distro
const { useAzureMonitor } = require('@azure/monitor-opentelemetry');
const { trace } = require('@opentelemetry/api');

useAzureMonitor({
  azureMonitorExporterOptions: { connectionString: process.env.APPLICATIONINSIGHTS_CONNECTION_STRING },
  samplingRatio: 0.2,      // keep 20 percent, correlation preserved
});

const tracer = trace.getTracer('orders');
await tracer.startActiveSpan('settleInvoice', async (span) => {
  span.setAttribute('tenant.id', tenantId);
  try { await settle(); } finally { span.end(); }
});

// KQL on sampled data: sum(itemCount), never count()
// requests
// | where timestamp > ago(1h)
// | summarize actual = sum(itemCount), retained = count() by cloud_RoleName

Key Points

  • Adaptive sampling is on by default and preserves whole operations, not random items
  • Use sum(itemCount) in KQL, count() undercounts by the sampling factor
  • W3C traceparent carries correlation into operation_Id and operation_ParentId
  • Prefer the Azure Monitor OpenTelemetry distribution for new services
Q34

What does Azure Front Door do that a CDN does not, and how do its health probes and caching behave?

IntermediateEdge and Delivery

Answer

Front Door is a global layer 7 reverse proxy on Microsoft's anycast edge, so it does content delivery, but the delivery is the smaller half. TLS terminates at the nearest point of presence, which shortens the handshake round trips for a user in Chennai hitting an origin in Central India and matters far more for a user in Singapore. From there Front Door applies WAF policies with Microsoft-managed rule sets plus custom rules including rate limiting and geo-filtering, runs a rules engine that can rewrite paths, add security headers, and redirect, and routes to an origin group.

Origin selection uses priority first (lower priority number wins while healthy) and then weight within the same priority, with latency-based selection among origins whose measured latency falls inside a configurable sensitivity band. Health probes are the operational detail interviewers probe: you set a probe path, protocol, and interval, and Front Door marks an origin healthy only after a required number of successful samples out of a sample size, so the actual time to eject a broken origin is roughly interval multiplied by sample size, not one failed probe. Probes are sent from many points of presence, so a naive health endpoint that hits the database on every call gets a surprising amount of traffic, and the correct design is a cheap liveness path that does not fan out to dependencies.

Caching is opt-in per route, honours cache-control headers, supports query string handling modes and compression, and purge is by path or wildcard. Compared to a plain CDN, what you additionally get is origin failover, WAF at the edge, private link to the origin on the Premium tier, and session affinity.

az afd endpoint create -g rg-edge --profile-name afd-prod \
  --endpoint-name ep-www --enabled-state Enabled

az afd origin create -g rg-edge --profile-name afd-prod \
  --origin-group-name og-api --origin-name origin-cin \
  --host-name api-cin.azurewebsites.net --origin-host-header api-cin.azurewebsites.net \
  --priority 1 --weight 1000 --https-port 443 --enabled-state Enabled

az afd origin create -g rg-edge --profile-name afd-prod \
  --origin-group-name og-api --origin-name origin-sin \
  --host-name api-sin.azurewebsites.net --priority 2 --weight 1000

# Ejection time is roughly interval * sample-size, not one bad probe
az afd origin-group update -g rg-edge --profile-name afd-prod \
  --origin-group-name og-api --probe-path /healthz --probe-interval-in-seconds 30 \
  --sample-size 4 --successful-samples-required 3

az afd endpoint purge -g rg-edge --profile-name afd-prod \
  --endpoint-name ep-www --content-paths "/static/*"

Key Points

  • Anycast edge TLS termination, WAF, rules engine, caching and origin failover in one service
  • Priority selects the active origin, weight distributes within a priority tier
  • Time to eject a bad origin is roughly probe interval times sample size
  • Health endpoints get probed from many POPs, keep them cheap
Q35

How do blob versioning, soft delete, lifecycle management and immutability policies interact?

IntermediateStorage

Answer

These are four independent features that together define your data protection story, and they interact in ways that surprise people on the bill. Blob soft delete retains deleted blobs and overwritten versions for a configured number of days so they can be undeleted, and container soft delete does the same at container level. Blob versioning goes further: every write creates a new immutable version, and the current version is just the newest, which makes accidental overwrite fully recoverable but also means a workload that rewrites the same blob every minute quietly accumulates thousands of billed versions.

That is the classic surprise invoice, and the fix is a lifecycle rule that expires non-current versions after a small number of days. Lifecycle management is a JSON policy on the account with rules that filter by prefix and blob type and then apply actions to baseBlob, snapshot, and version separately: tierToCool, tierToCold, tierToArchive, delete, and enableAutoTierToHotFromCool. Rules run once per day and are evaluated against last-modified time or last-accessed time if access tracking is enabled, so nothing happens instantly and testing requires patience.

Immutability is the compliance layer: a time-based retention policy or a legal hold, set at container or version scope, makes blobs write-once-read-many so that even an account owner cannot delete or overwrite them until retention expires. In locked mode the policy itself cannot be shortened, only extended, which is precisely what auditors in Indian BFSI want to see. Note that an immutability policy defeats lifecycle deletion, and point-in-time restore requires versioning, soft delete, and change feed all enabled.

az storage account blob-service-properties update -n stdata001 -g rg-data \
  --enable-versioning true \
  --enable-delete-retention true --delete-retention-days 30 \
  --enable-change-feed true --enable-restore-policy true --restore-days 14

# lifecycle.json: control versions or the bill grows quietly
# {"rules":[{"enabled":true,"name":"tier-and-prune","type":"Lifecycle",
#   "definition":{
#     "filters":{"blobTypes":["blockBlob"],"prefixMatch":["raw/"]},
#     "actions":{
#       "baseBlob":{"tierToCool":{"daysAfterModificationGreaterThan":30},
#                   "tierToArchive":{"daysAfterModificationGreaterThan":180}},
#       "version":{"delete":{"daysAfterCreationGreaterThan":7}}}}}]}

az storage account management-policy create -n stdata001 -g rg-data \
  --policy @lifecycle.json

# WORM for audit data, locked mode cannot be shortened
az storage container immutability-policy create -g rg-data \
  --account-name stdata001 -c audit --period 2555

Key Points

  • Versioning makes every overwrite a billed object, prune non-current versions with lifecycle
  • Lifecycle rules run about once a day, they are never instant
  • Immutability blocks deletion including lifecycle deletes, locked mode can only be extended
  • Point-in-time restore needs versioning, soft delete and change feed together
Q36

Design a hub-and-spoke network. Why is VNet peering non-transitive and what does that force you to build?

IntermediateNetworking

Answer

The hub-and-spoke topology puts shared services in a hub virtual network (Azure Firewall or a network virtual appliance, VPN or ExpressRoute gateway, DNS Private Resolver, Bastion, private endpoints for shared PaaS) and gives each workload or environment its own spoke, peered to the hub. Peering is a layer 3 connection with no gateway or appliance in the path, so it is high bandwidth and low latency, but it is explicitly non-transitive: if spoke A peers with the hub and spoke B peers with the hub, A cannot reach B. Azure will not route it, whatever the NSGs say.

You get spoke-to-spoke connectivity in one of three ways: peer the spokes directly (simple, but the number of peerings grows quadratically and you lose central inspection), route spoke traffic through a hub appliance using user-defined routes plus IP forwarding, or use Azure Virtual WAN, which manages the routing for you. The UDR approach is the classic enterprise answer, because it forces all east-west traffic through Azure Firewall where it can be logged and filtered by FQDN. The route table on each spoke subnet carries a 0.0.0.0/0 route with next hop VirtualAppliance pointing at the firewall's private IP, and you must also allow the gateway to learn spoke routes by setting useRemoteGateways on the spoke and allowGatewayTransit on the hub. Two details generate real incidents: forcing 0.0.0.0/0 through the firewall also captures traffic to Azure PaaS unless you add more specific routes or service tags, and address spaces must never overlap, because peering refuses to create when they do and renumbering a live spoke is a project, not a change request.

# Peering is per direction and non-transitive
az network vnet peering create -g rg-net -n hub-to-spoke1 \
  --vnet-name vnet-hub --remote-vnet $SPOKE1_ID \
  --allow-vnet-access --allow-forwarded-traffic --allow-gateway-transit

az network vnet peering create -g rg-net -n spoke1-to-hub \
  --vnet-name vnet-spoke1 --remote-vnet $HUB_ID \
  --allow-vnet-access --allow-forwarded-traffic --use-remote-gateways

# Force east-west and egress through the firewall
FW_IP=$(az network firewall show -g rg-net -n afw-hub --query "ipConfigurations[0].privateIPAddress" -o tsv)
az network route-table create -g rg-net -n rt-spoke1
az network route-table route create -g rg-net --route-table-name rt-spoke1 \
  -n default --address-prefix 0.0.0.0/0 \
  --next-hop-type VirtualAppliance --next-hop-ip-address $FW_IP

az network vnet subnet update -g rg-net --vnet-name vnet-spoke1 \
  -n snet-app --route-table rt-spoke1

Key Points

  • Peering is non-transitive, spoke to spoke needs direct peering, UDR via a hub NVA, or Virtual WAN
  • allowGatewayTransit on the hub plus useRemoteGateways on the spoke shares the gateway
  • A 0.0.0.0/0 UDR to the firewall also captures PaaS traffic unless you add specific routes
  • Overlapping address spaces make peering impossible, plan the IPAM first
Q37

How do Cosmos DB multi-region writes handle conflicts, and what breaks when you enable them?

AdvancedCosmos DB

Answer

With a single write region, every write is ordered by one primary and replicas apply that order, so conflicts cannot exist. Enabling multi-region writes turns every configured region into a write region with local low-latency writes, and the price is that two regions can accept conflicting writes to the same document id in the same logical partition within the replication window. Cosmos resolves them with a conflict resolution policy defined per container.

Last Write Wins is the default and picks the highest value of a numeric or timestamp path, defaulting to the system _ts property, which has a one-second granularity, so two writes in the same second resolve arbitrarily. Custom resolution runs a stored procedure you supply, and if the procedure errors or is absent, the losing documents land in the conflicts feed, a read-only feed your application must drain or the conflicts are silently lost after their retention. The consistency implications are the part senior interviews focus on: Strong consistency is not available with multi-region writes, and Bounded Staleness carries additional restrictions, so in practice you are running Session or weaker.

Session consistency in a multi-write topology means read-your-own-writes only holds when the client presents its session token, so if your service is stateless behind a load balancer and a follow-up request lands on a different instance, you must propagate the token yourself or the user sees stale data. Cost roughly doubles for writes because they are billed in every write region. The honest engineering answer in interviews is that most Indian applications serving a single geography should use a single write region with read regions, and adopt multi-region writes only when write latency across continents is a real product requirement.

// Custom conflict resolution: highest version wins, else keep the richer doc
await database.containers.createIfNotExists({
  id: 'carts',
  partitionKey: { paths: ['/tenantId'] },
  conflictResolutionPolicy: {
    mode: 'Custom',
    conflictResolutionProcedure: 'dbs/shop/colls/carts/sprocs/resolveCart',
  },
});

// Propagate the session token across stateless instances or reads go stale
const { resource, headers } = await container.items.create(doc);
const token = headers['x-ms-session-token'];

const read = await container.item(doc.id, doc.tenantId).read({
  sessionToken: token,
});

// Anything the sproc could not resolve lands here and must be drained
const conflicts = await container.conflicts.readAll().fetchAll();

Key Points

  • LWW on _ts has one-second granularity, ties resolve arbitrarily
  • Custom resolution runs a stored procedure, failures go to the conflicts feed
  • Strong consistency is unavailable with multi-region writes
  • Session guarantees need the session token carried across stateless instances
Q38

How should a production service handle 429 throttling from Azure services, and what does the platform tell you?

AdvancedReliability

Answer

Throttling is normal operating behaviour on Azure, not an exception, and every managed service signals it differently. Cosmos DB returns HTTP 429 with x-ms-retry-after-ms giving the exact wait in milliseconds, and the SDK retries automatically up to a configurable count before surfacing the error. Azure Storage returns 503 ServerBusy or 500 OperationTimedOut when it exceeds the per-account ingress, egress, or transaction limits, and expects exponential backoff.

ARM control-plane calls return 429 with a Retry-After header plus x-ms-ratelimit-remaining-subscription-reads so you can see how close a script is to the limit. Azure SQL surfaces resource governance as specific error numbers, notably 10928 and 10929 for session and worker limits and 40501 for engine throttling, and connection pool exhaustion in the client looks similar but is not the same problem. Event Hubs and Service Bus return ServerBusy exceptions from the AMQP layer.

The correct client pattern is exponential backoff with full jitter (deterministic backoff synchronises all your instances into a retry stampede), a bounded retry budget rather than unlimited retries, honouring any Retry-After the service supplies instead of your own formula, and a circuit breaker so a persistently throttled dependency fails fast instead of consuming every worker thread. Retries must also be safe: only retry idempotent operations, or make them idempotent with a client-supplied id such as a Service Bus messageId for duplicate detection or a unique key in Cosmos. Finally, treat sustained 429s as a capacity signal, not a client bug. If a Cosmos container throttles for hours, the fix is autoscale, a better partition key, or reduced RU per operation, not a longer retry loop.

// Full jitter backoff that honours the service's own hint
async function withRetry(fn, { attempts = 5, baseMs = 100, capMs = 8000 } = {}) {
  for (let i = 0; ; i++) {
    try {
      return await fn();
    } catch (err) {
      const status = err.statusCode ?? err.code;
      const retryable = status === 429 || status === 503 || status === 500;
      if (!retryable || i >= attempts - 1) throw err;

      const hinted = Number(err.retryAfterInMs ?? err.headers?.['retry-after'] ?? 0);
      const expo = Math.min(capMs, baseMs * 2 ** i);
      const delay = hinted > 0 ? hinted : Math.random() * expo;  // full jitter
      await new Promise((r) => setTimeout(r, delay));
    }
  }
}

// Cosmos SDK: bound the built-in retries instead of letting them stack
const client = new CosmosClient({
  endpoint, aadCredentials: cred,
  connectionPolicy: { retryOptions: { maxRetryAttemptCount: 3, maxWaitTimeInSeconds: 10 } },
});

Key Points

  • Cosmos 429 carries x-ms-retry-after-ms, Storage signals 503 ServerBusy, SQL uses 10928/10929/40501
  • Full jitter prevents synchronised retry stampedes across your fleet
  • Only retry idempotent work, or make it idempotent with a client-supplied id
  • Sustained throttling is a capacity problem, not something a longer retry loop fixes
Q39

What are the main production failure modes of an AKS cluster, and how do you prevent each?

AdvancedAKS

Answer

Five recur constantly. First, IP exhaustion in the node subnet, which stops the cluster autoscaler dead and shows up as pods stuck Pending with FailedScheduling; prevent it with CNI Overlay or a generously sized subnet that accounts for upgrade surge nodes. Second, SNAT port exhaustion.

The Standard Load Balancer allocates a fixed number of SNAT ports per node against each outbound public IP, and a service that opens many short-lived outbound connections to the same destination burns through them, producing intermittent connection timeouts that look like a downstream failure. The fix is a NAT Gateway on the node subnet, or more outbound IPs, plus connection pooling and keep-alive in the application. Third, disruptive upgrades.

Node pool upgrades cordon and drain one surge node at a time by default, so a Deployment with a single replica, or a PodDisruptionBudget that permits zero disruptions, either causes downtime or blocks the drain until it times out. Set maxSurge higher for speed, run at least two replicas with anti-affinity across zones, and write PDBs that actually allow disruption. Fourth, spot node evictions, which arrive with 30 seconds notice: keep spot in a separate node pool with a taint, tolerate it only from interruptible workloads, and never run stateful sets or the ingress controller there.

Fifth, resource starvation from missing requests and limits, where one pod without a memory limit triggers node pressure and the kubelet evicts unrelated pods; enforce requests and limits with a policy and watch for OOMKilled in pod status. Beyond these, keep the Kubernetes version inside the supported window, since AKS supports roughly the current minor version and the two before it, and an unsupported cluster loses both support and automatic patching.

# SNAT exhaustion: give the node subnet a NAT Gateway instead of LB SNAT
az network nat gateway create -g rg-net -n natgw-aks \
  --public-ip-addresses pip-nat --idle-timeout 10
az network vnet subnet update -g rg-net --vnet-name vnet-prod \
  -n snet-aks --nat-gateway natgw-aks

# Faster, safer upgrades
az aks nodepool update -g rg-aks --cluster-name aks-prod -n system \
  --max-surge 33%

# Spot pool: tainted so only tolerant workloads land there
az aks nodepool add -g rg-aks --cluster-name aks-prod -n spotpool \
  --priority Spot --eviction-policy Delete --spot-max-price -1 \
  --node-taints kubernetes.azure.com/scalesetpriority=spot:NoSchedule \
  --enable-cluster-autoscaler --min-count 0 --max-count 20

kubectl get pdb -A
kubectl get pods -A --field-selector status.phase=Pending

Key Points

  • Pod IP exhaustion stops the autoscaler, CNI Overlay or a large subnet prevents it
  • SNAT port exhaustion looks like downstream flakiness, fix it with a NAT Gateway
  • Single-replica Deployments and zero-disruption PDBs turn upgrades into outages
  • Spot nodes need taints and tolerations, never host stateful workloads there
Q40

How does an App Service slot swap achieve zero downtime, and what makes a swap take the site down anyway?

AdvancedApp Service

Answer

A swap is not a DNS change or a traffic cut-over, it is a warm-up followed by an atomic routing switch. When you initiate a swap, App Service first applies the target slot's sticky configuration to the source slot instances and restarts them, then sends warm-up requests to those instances and waits for a successful response, and only when every instance responds does it swap the virtual IP routing between the slots. If warm-up fails, the swap is cancelled and production is untouched.

That is also where the failures come from. Application settings and connection strings are swapped by default unless you mark them as slot settings, and the classic incident is a staging slot pointed at a staging database whose connection string is not marked sticky, so after the swap production reads from staging. Conversely, marking too much as sticky means the settings restart the source slot at swap time and can undo the warm-up you just paid for.

The second failure is warm-up that does not actually warm anything: the default probe hits the site root, so if your root returns 200 from a static file while the real cost is in DI container construction, EF Core model building, or JIT of the first controller, the swap succeeds and the first real user still eats a ten-second request. Set WEBSITE_SWAP_WARMUP_PING_PATH to an endpoint that exercises the expensive path, and WEBSITE_SWAP_WARMUP_PING_STATUSES to the codes you accept. The third is ARR affinity, which pins sessions with a cookie and interacts badly with the instance restart, so disable it for stateless APIs. Auto swap is available for continuous deployment, but for production the safer shape is deploy to staging, run smoke tests against the staging hostname, then swap, with swap-with-preview if you need to validate under production configuration first.

az webapp deployment slot create -g rg-web -n app-orders-prod --slot staging

# Mark environment-specific settings sticky so they never travel with the swap
az webapp config appsettings set -g rg-web -n app-orders-prod --slot staging \
  --slot-settings ASPNETCORE_ENVIRONMENT=Staging SqlConnection=$STAGING_CONN

# Warm the expensive path, not a static root page
az webapp config appsettings set -g rg-web -n app-orders-prod --slot staging --settings \
  WEBSITE_SWAP_WARMUP_PING_PATH=/health/warmup \
  WEBSITE_SWAP_WARMUP_PING_STATUSES=200,202

az webapp config set -g rg-web -n app-orders-prod --client-affinity-enabled false

# Validate under production config, then commit
az webapp deployment slot swap -g rg-web -n app-orders-prod --slot staging --action preview
az webapp deployment slot swap -g rg-web -n app-orders-prod --slot staging --action swap

Key Points

  • Swap = apply target config, restart, warm up, then switch routing atomically
  • Settings swap by default, use --slot-settings for anything environment-specific
  • Default warm-up pings the root, set WEBSITE_SWAP_WARMUP_PING_PATH to a real path
  • Disable ARR affinity for stateless APIs, and prefer swap-with-preview for risky releases
💡 Pro Tip: After every swap, verify one production request actually reads the production database. A silent staging-connection swap can run for days before anyone notices missing orders.
Q41

Design multi-region disaster recovery for an Azure application. What sets the real RTO and RPO?

AdvancedArchitecture

Answer

Start by writing down the RTO and RPO the business will actually fund, because those two numbers determine the architecture and nothing else does. Active-passive with a warm standby is the common enterprise answer: infrastructure deployed in the secondary region by the same Bicep or Terraform, scaled down, with the data tier replicating continuously. Active-active is more expensive and more complex, and it forces you to solve write conflicts and data residency, which for regulated Indian workloads may be constrained to Indian regions by RBI or IRDAI rules and by internal data localisation policies.

The stateless tier is the easy part: Front Door with priority routing gives you automatic failover within a probe cycle. The data tier is what sets the real numbers. Azure SQL auto-failover groups replicate asynchronously with an RPO typically measured in seconds and an automatic failover grace period you configure in hours; Cosmos DB with a secondary read region and automatic failover offers a low RPO under session consistency; GZRS storage replicates asynchronously with an RPO usually under 15 minutes and needs a customer-initiated account failover.

Anything not replicated at all, cached state, disks on IaaS VMs without Azure Site Recovery, secrets that live only in a regional Key Vault, is your true RTO. The list people forget: Key Vault must exist in the secondary (vaults are regional, though they fail over transparently within a region pair for reads), managed identities and role assignments must be created in the secondary, private DNS zones and private endpoints must be duplicated, subscription quotas in the secondary region must be raised in advance because capacity is not reserved by hoping, and TLS certificates must be present. Then test it. A DR plan that has never been executed is a document, not a capability, and mature teams run a scheduled failover drill at least twice a year.

# Priority routing: secondary only takes traffic when primary probes fail
az afd origin update -g rg-edge --profile-name afd-prod \
  --origin-group-name og-api --origin-name origin-cin --priority 1
az afd origin update -g rg-edge --profile-name afd-prod \
  --origin-group-name og-api --origin-name origin-sin --priority 2

# Data tier: this is what actually sets RPO
az sql failover-group show -n fog-orders -g rg-db -s sqlsrv-prod \
  --query "{policy:readWriteEndpoint.failoverPolicy, grace:readWriteEndpoint.failoverWithDataLossGracePeriodMinutes}"

az cosmosdb update -n cosmos-prod -g rg-db --enable-automatic-failover true
az cosmosdb failover-priority-change -n cosmos-prod -g rg-db \
  --failover-policies centralindia=0 southindia=1

# Quota in the DR region is not reserved, request it before you need it
az vm list-usage --location southindia -o table

Key Points

  • RTO and RPO drive the design, not the other way around
  • Front Door handles the stateless failover, the data tier sets the real numbers
  • Duplicate the invisible dependencies: Key Vault, role assignments, private DNS, certificates, quota
  • An untested DR plan is a document, drill it on a schedule
Q42

An Azure bill jumped 3x overnight with no traffic change. How do you find the cause?

AdvancedCost Engineering

Answer

Work top down and never guess. Open Cost Analysis, set the granularity to daily, group by service name, and find the exact day and the exact service. Then group by resource to isolate the resource, and by meter to isolate what within that resource changed.

Six causes account for most of these incidents. Log Analytics ingestion is the leader: somebody enabled a verbose diagnostic category, turned on AKS container insights with default settings, or shipped debug logs to production, and ingestion is billed per gigabyte. The Usage table tells you exactly which table is responsible, and the remedies are data collection rule filters, the Basic or Auxiliary log plans for high-volume low-query data, a commitment tier, and shorter interactive retention with archive after that.

Second is Cosmos DB autoscale, where a bad query or a missing partition key filter drove RU to the configured maximum and it stayed there, since autoscale bills for the peak in each hour. Third is bandwidth: cross-region egress from a chatty service that got deployed into the wrong region, or a data pipeline reading from a storage account in another geography. Fourth is Application Insights with sampling accidentally disabled.

Fifth is orphaned resources after a failed deployment, unattached premium disks, idle Application Gateways, and public IPs. Sixth is a reservation or savings plan that expired, so the same workload silently reverted to pay-as-you-go. Once you find it, put a control in place: a budget with an action group, a Log Analytics daily cap, an anomaly alert, and a policy that requires cost tags so the next spike has an owner within a minute rather than a week.

// KQL: which table is eating the Log Analytics budget?
Usage
| where TimeGenerated > ago(14d) and IsBillable == true
| summarize GB = round(sum(Quantity) / 1024, 2) by DataType, bin(TimeGenerated, 1d)
| order by GB desc

// Which resource inside that table?
AzureDiagnostics
| where TimeGenerated > ago(1d)
| summarize records = count(), mb = sum(estimate_data_size(*)) / 1048576 by ResourceId, Category
| top 20 by mb desc

// Hard stop so a runaway logger cannot bill you twice
// az monitor log-analytics workspace update -g rg-obs -n law-prod --quota 50

// Reservation about to lapse and quietly triple a bill
// az reservations reservation-order list --query "[].{name:displayName, expiry:expiryDate}" -o table

Key Points

  • Cost Analysis daily, grouped by service, then resource, then meter
  • Log Analytics ingestion, Cosmos autoscale peaks and cross-region egress are the usual suspects
  • Use DCR filters, Basic/Auxiliary log plans and a daily cap to bound observability spend
  • Expired reservations silently revert workloads to pay-as-you-go pricing
Q43

How do you lock down a storage account so that stolen credentials are useless?

AdvancedSecurity

Answer

Defence here has four independent layers and a good answer names all of them. Layer one is identity: set allowSharedKeyAccess to false so the account keys and every key-signed SAS stop working entirely, leaving Entra ID as the only authentication path. This is the single highest-value change on a storage account, because it converts a leaked key in a Git repository from a breach into a nuisance, and it forces every caller onto managed identities with data-plane roles.

Layer two is network: set publicNetworkAccess to Disabled and reach the account through private endpoints, or, if you cannot, use the account firewall with specific virtual network rules and IP rules and be careful with the Allow Azure services on the trusted services list exception, which is broader than most people assume. Layer three is data protection: enforce minimum TLS 1.2, require HTTPS only, disable public blob access on every container, enable soft delete, versioning, and an immutability policy for anything regulated. Layer four is detection: send StorageRead, StorageWrite and StorageDelete diagnostic logs to a workspace, turn on Microsoft Defender for Storage which flags anomalous access patterns and malware uploads, and alert on any key rotation or firewall change through an Azure Monitor activity log alert.

Enforce all of it with Azure Policy at the management group so a new account cannot be created without the settings, rather than relying on a checklist. A senior candidate will also mention the operational cost: disabling shared keys breaks tools that only speak account keys, including some older backup utilities, AzCopy invocations using a key, and Terraform state backends configured that way, so the migration needs an inventory of callers first.

az storage account update -n stdata001 -g rg-data \
  --allow-shared-key-access false \
  --allow-blob-public-access false \
  --min-tls-version TLS1_2 \
  --https-only true \
  --public-network-access Disabled \
  --default-action Deny

# Who was still using the account key before you flip the switch?
# StorageBlobLogs
# | where TimeGenerated > ago(30d) and AuthenticationType == "AccountKey"
# | summarize calls = count() by CallerIpAddress, UserAgentHeader
# | order by calls desc

az monitor diagnostic-settings create -n diag-blob \
  --resource "$(az storage account show -n stdata001 --query id -o tsv)/blobServices/default" \
  --workspace $WORKSPACE_ID \
  --logs '[{"category":"StorageRead","enabled":true},{"category":"StorageWrite","enabled":true},{"category":"StorageDelete","enabled":true}]'

Key Points

  • allowSharedKeyAccess=false makes leaked keys and key-signed SAS useless
  • Private endpoint plus Deny default action removes the public data path
  • Enforce TLS 1.2, HTTPS only, no public containers, soft delete and immutability
  • Audit AuthenticationType in StorageBlobLogs before disabling keys, tooling will break
Q44

Explain Event Hubs partitions, checkpointing and consumer group rebalancing. Where does ordering actually hold?

AdvancedStreaming

Answer

An event hub is an append-only log split into partitions fixed at creation for Standard tier (Premium and Dedicated allow increases, and doing so changes the hash distribution for future events). Order is guaranteed within a partition and nowhere else, so if events for one order id must be processed in sequence you must set a partition key that hashes them to the same partition; publishing without a key round-robins for throughput and gives you no ordering at all. Partition count is also your parallelism ceiling: within a consumer group, a partition is owned by exactly one consumer at a time, so 8 partitions means at most 8 useful consumer instances, and adding a ninth just leaves it idle.

Consumers track progress with checkpoints, an offset and sequence number written to a checkpoint store, normally a blob container, on a cadence you control. Checkpointing is not per event: the standard pattern is to checkpoint every N events or every few seconds, which means a crash replays everything since the last checkpoint, so processing must be idempotent. Checkpoint too often and the blob store becomes the bottleneck; too rarely and recovery replays minutes of data.

When instances join or leave, the event processor rebalances partition ownership using leases in the same blob container, and during the rebalance a partition can be briefly processed by two consumers, which is another reason for idempotency. Consumer groups are independent views of the same log with their own checkpoints, capped at 20 on Standard, which is how you fan out to a real-time consumer and an archival consumer without them interfering. Capture writes raw Avro batches to blob or Data Lake on a size or time trigger, and it is the cheapest way to keep a replayable archive beyond the retention window.

const { EventHubConsumerClient } = require('@azure/event-hubs');
const { ContainerClient } = require('@azure/storage-blob');
const { BlobCheckpointStore } = require('@azure/eventhubs-checkpointstore-blob');

const store = new BlobCheckpointStore(
  new ContainerClient(blobUrl, cred),   // leases + checkpoints live here
);

const client = new EventHubConsumerClient('analytics', ns, 'clickstream', store, cred);

client.subscribe({
  async processEvents(events, ctx) {
    if (!events.length) return;
    for (const e of events) await handleIdempotently(e);
    // checkpoint per batch, not per event
    await ctx.updateCheckpoint(events[events.length - 1]);
  },
  async processError(err, ctx) {
    console.error(ctx.partitionId, err);
  },
}, { maxBatchSize: 200, maxWaitTimeInSeconds: 5 });

Key Points

  • Ordering holds only within a partition, so a partition key is required for per-entity order
  • Partition count caps consumer parallelism inside a consumer group
  • Checkpoints are periodic, so replay after a crash is normal and handlers must be idempotent
  • Rebalancing can briefly double-process a partition, Capture gives you a cheap replay archive
Q45

An Azure SQL Database is slow but CPU looks fine. How do you diagnose and fix it?

AdvancedDatabase Performance

Answer

Start by separating resource governance from query problems, because Azure SQL throttles on several dimensions and CPU is only one. Query sys.dm_db_resource_stats, which samples every 15 seconds for the last hour, and look at avg_cpu_percent, avg_data_io_percent, avg_log_write_percent, and max_worker_percent together. High log write percent with normal CPU means you are hitting the log throughput governor, which is a hard ceiling per service tier and is the usual cause of a slow bulk load that looks like nothing is happening.

High worker percent means session or worker exhaustion, which surfaces to clients as error 10928. Next, look at waits with sys.dm_db_wait_stats, which in Azure SQL is scoped to the database and resets on failover. LOG_RATE_GOVERNOR, SOS_SCHEDULER_YIELD, PAGEIOLATCH_SH, and the various LCK_M waits each point somewhere different, and specific Azure governance waits are named for what they govern.

Query Store is the most valuable tool and is on by default: it retains query plans and runtime statistics, exposes regressed queries after a plan change, and lets you force a known-good plan with sp_query_store_force_plan, which is the fastest mitigation for a parameter-sniffing regression at 2 AM. Automatic tuning can do that for you with the FORCE_LAST_GOOD_PLAN option, and can create and drop indexes if you enable it. Beyond that, the usual suspects apply: missing indexes visible in the missing index DMVs, implicit conversions caused by a mismatched parameter type that silently defeats an index seek, an ORM emitting N+1 queries that no amount of tuning fixes, and connection pool exhaustion in the application that presents as database slowness. For read-heavy workloads, Business Critical and Hyperscale offer read scale-out through ApplicationIntent=ReadOnly, which moves reporting traffic off the primary at no extra cost.

-- Which governor am I actually hitting?
SELECT TOP 20 end_time,
       avg_cpu_percent, avg_data_io_percent,
       avg_log_write_percent, max_worker_percent, avg_memory_usage_percent
FROM sys.dm_db_resource_stats
ORDER BY end_time DESC;

-- What is the workload waiting on?
SELECT TOP 15 wait_type, wait_time_ms / 1000.0 AS wait_s, waiting_tasks_count
FROM sys.dm_db_wait_stats
WHERE wait_type NOT IN ('SLEEP_TASK','XE_TIMER_EVENT','BROKER_TASK_STOP')
ORDER BY wait_time_ms DESC;

-- Regressed queries from Query Store, then force the good plan
SELECT q.query_id, p.plan_id, rs.avg_duration / 1000.0 AS avg_ms, rs.count_executions
FROM sys.query_store_runtime_stats rs
JOIN sys.query_store_plan p ON p.plan_id = rs.plan_id
JOIN sys.query_store_query q ON q.query_id = p.query_id
ORDER BY rs.avg_duration DESC;

EXEC sp_query_store_force_plan @query_id = 4211, @plan_id = 9032;
ALTER DATABASE CURRENT SET AUTOMATIC_TUNING (FORCE_LAST_GOOD_PLAN = ON);

Key Points

  • Check CPU, data IO, log write and worker percent together, log rate is a common hidden ceiling
  • sys.dm_db_wait_stats is database-scoped in Azure SQL and resets on failover
  • Query Store plus sp_query_store_force_plan is the fastest fix for a plan regression
  • ApplicationIntent=ReadOnly moves reporting off the primary on Business Critical and Hyperscale

Companies Hiring Azure

Microsoft
Accenture
TCS
Infosys
Wipro
Capgemini
LTIMindtree
HCLTech

Salary Insights

Average in India
₹8-26 LPA

Frequently Asked Questions

What does an Azure engineer earn in India in 2026?

Broadly ₹8-26 LPA depending on depth and role. A cloud support or L1/L2 operations engineer in a large services firm typically starts at ₹3.5-6 LPA. An Azure administrator or DevOps engineer with two to five years and hands-on Bicep, AKS, and pipelines lands around ₹9-16 LPA. Cloud architects and platform engineers who can design landing zones, private networking, and multi-region DR command ₹20-35 LPA, and product companies in Bengaluru, Hyderabad, and Pune pay meaningfully above services firms for the same years of experience. The biggest single multiplier is proving you have run production, not that you have completed a lab.

How long does it take to prepare for an Azure interview?

If you already work with any cloud, four to six weeks of focused effort is realistic: two weeks on the resource model, identity, networking, and storage, two weeks building something end to end with Bicep and a pipeline, and two weeks on the failure modes in the advanced section of this page. Starting from zero cloud experience, plan on three to four months. The fastest route is to keep a personal subscription with the free credit, deploy a small application behind Front Door with a private-endpoint database, break it deliberately, and read the errors. Candidates who can describe an outage they debugged interview far better than candidates who have only read documentation.

What is expected from a fresher versus an experienced Azure candidate?

Freshers are assessed on the resource hierarchy, core services, and whether they can reason about cost and availability at all: expect AZ-900 level breadth plus one AZ-104 level depth area, plus real scripting ability in Bash, PowerShell, or Python. Nobody expects a fresher to have designed a hub-and-spoke topology. From three years onward, the questions shift to trade-offs and incidents: why you chose a private endpoint over a service endpoint, how you sized an AKS node subnet, what your rollback plan was, how you found a cost spike. Beyond six years, interviews are almost entirely architecture, governance, and blast-radius reasoning, with a strong bias toward candidates who have owned a migration or a production incident end to end.

Is Azure still worth learning in 2026, or should I learn AWS instead?

Both are worth learning, and in India the volume argument favours Azure more than global market-share numbers suggest, because so much enterprise hiring flows through Microsoft-centric estates: existing Windows Server and SQL Server licensing, Microsoft 365 and Entra ID for identity, and large services engagements migrating on-premises workloads. AWS tends to dominate product-company and startup hiring, Azure dominates enterprise and BFSI. If you have no constraint, pick the one your target employers use. The underlying concepts, identity, networking, storage durability, managed databases, and container orchestration, transfer almost completely, so the second cloud takes a fraction of the time the first did.

Which Azure certification actually helps at interview time?

AZ-104 (Administrator) is the one that reliably clears resume screens in Indian services firms, and AZ-305 (Solutions Architect Expert) is the one that opens architect conversations. AZ-204 suits developers building on App Service, Functions, and Cosmos DB, AZ-400 suits DevOps roles, DP-203 suits data engineering, and AZ-500 suits security. Be honest about what a certificate does: it gets you into the room, and it is worth close to nothing in the room. Interviewers in 2026 assume certification content is easy to memorise, so the follow-up is always a scenario, and a certified candidate who cannot explain why a private endpoint failed to resolve loses to an uncertified one who can.

How much Kubernetes and Terraform do I need for an Azure role?

For an Azure DevOps or platform role in 2026, both are effectively mandatory. You should be able to read a Deployment manifest, explain requests and limits, debug a Pending pod, and describe how ingress reaches a service. On infrastructure as code, know Bicep because it is the Azure-native answer and shows up in Microsoft-shop interviews, and know Terraform because most Indian enterprises with multi-cloud or hybrid estates standardise on it. For a pure Azure administrator or support role, deep Kubernetes is optional but IaC is not, since manual portal work is now treated as a red flag in any team that runs production.

Introduction

Azure is the cloud that dominates Indian enterprise IT. Every large services firm running Microsoft-shop migrations, every BFSI customer that already licenses Windows Server and SQL Server, and every organisation that standardised on Microsoft Entra ID for identity ends up on Azure by gravity. That makes the Azure job market in India unusually broad: the same platform pays for a support engineer patching virtual machines in a managed-services pod and for a principal architect designing a multi-region landing zone with private endpoints, Azure Policy guardrails, and a hub-and-spoke network for a regulated bank.

Interviewers in 2026 have stopped asking candidates to recite service names. They ask what breaks. Why a private endpoint resolves to a public IP from an on-premises subnet, why a role assignment that looks correct still returns AuthorizationPermissionMismatch on a blob, why a Cosmos DB container that was fine at 400 RU/s starts throwing 429s the day a single tenant grows past a hot partition, why an AKS cluster runs out of pod IPs after one node pool scale-out, and why a slot swap took the site down instead of warming it. Those are the questions that decide offers.

This guide covers the 45 Azure interview questions that actually get asked, ordered from fundamentals through production failure modes. Each answer explains the real behaviour of the platform, the gotcha that bites teams in production, and what the interviewer is probing for underneath the question. Most technical answers carry a runnable Azure CLI command, Bicep snippet, SDK call, or KQL query. Work through the basic section to lock the resource model and identity story, then spend your time in the intermediate and advanced sections, which is where senior Azure interviews are won or lost.

Ready to practice Azure interviews?

Don't just read, practice these Azure questions live with an AI interviewer that asks follow-ups and scores your answers.

AI-powered practice
Instant feedback
Free to start
Start Free Mock Interview