← Back to Portal

AZ-104 Study Manual

Complete 2026 exam study guide — identities, storage, compute, networking, and monitoring for Azure administrators.

📘 AZ-104 EXAM STUDY GUIDE 2026

Deep Dive with Portal, Azure CLI, and PowerShell Steps

Exam: AZ-104 — Microsoft Azure Administrator Associate
Skills measured: April 17, 2026 | Pass: 700/1000 | Cost: $165 USD | Delivery: Pearson VUE

HOW TO USE THIS GUIDE

Each section provides administrator implementation steps — the tasks you perform on the job and in lab-based exam questions. Work through Portal steps first, then CLI, then PowerShell.

Prerequisites:

az login
az account set --subscription "<your-subscription-id>"
Connect-AzAccount
Set-AzContext -Subscription "<your-subscription-id>"

DOMAIN 1: MANAGE MICROSOFT ENTRA IDENTITIES AND GOVERNANCE (20–25%)

1.1 Create and Manage Users

Portal

  1. 1. Microsoft Entra admin center → Users → New user → Create user
  2. 2. Set User principal name (UPN), display name, password (auto-generate or manual)
  3. 3. Assign groups and roles as needed
  4. 4. For guest users: New guest user → invite via email (B2B)

Azure CLI

# Create cloud-only user
az ad user create \
  --display-name "Jane Admin" \
  --password '<TempP@ssw0rd!>' \
  --user-principal-name jane.admin@contoso.com \
  --force-change-password-next-sign-in true

# List users
az ad user list --query "[].{UPN:userPrincipalName, Name:displayName}" -o table

# Reset password
az ad user update --id jane.admin@contoso.com \
  --force-change-password-next-sign-in true \
  --password '<NewP@ssw0rd!>'

# Delete user
az ad user delete --id jane.admin@contoso.com

PowerShell (Microsoft Graph)

Connect-MgGraph -Scopes "User.ReadWrite.All"

$params = @{
    AccountEnabled = $true
    DisplayName = "Jane Admin"
    MailNickname = "janeadmin"
    UserPrincipalName = "jane.admin@contoso.com"
    PasswordProfile = @{
        ForceChangePasswordNextSignIn = $true
        Password = "<TempP@ssw0rd!>"
    }
}
New-MgUser -BodyParameter $params

1.2 Create and Manage Groups

Dynamic Group (requires Entra ID P1)

Connect-MgGraph -Scopes "Group.ReadWrite.All"

$params = @{
    DisplayName = "Finance Department"
    MailEnabled = $false
    MailNickname = "finance-dept"
    SecurityEnabled = $true
    GroupTypes = @("DynamicMembership")
    MembershipRule = '(user.department -eq "Finance")'
    MembershipRuleProcessingState = "On"
}
New-MgGroup -BodyParameter $params

Azure CLI

# Security group
az ad group create --display-name "IT-Admins" --mail-nickname "it-admins"

# Add member
az ad group member add --group "IT-Admins" --member-id <user-object-id>

# List members
az ad group member list --group "IT-Admins" -o table

1.3 Configure Self-Service Password Reset

Portal

  1. 1. Entra ID → Password reset → Properties → Enabled: All
  2. 2. Authentication methods → select 2 methods (mobile phone, email, security questions)
  3. 3. Registration → Require users to register when signing in: Yes
  4. 4. On-premises integration → Enable password writeback (requires PHS + P1)

PowerShell

# Check SSPR policy (via Graph beta)
Connect-MgGraph -Scopes "Policy.ReadWrite.Authorization"
# SSPR configured primarily via portal; verify with:
Get-MgPolicyAuthorizationPolicy

1.4 Configure Multi-Factor Authentication via Conditional Access

Portal

  1. 1. Entra ID → Security → Conditional Access → New policy
  2. 2. Name: Require MFA for All Users
  3. 3. Users: All users; Exclude: Break-glass emergency accounts
  4. 4. Cloud apps: All cloud apps
  5. 5. Grant: Require multifactor authentication
  6. 6. Enable policy: On (start with Report-only for testing)

PowerShell (Microsoft Graph)

Connect-MgGraph -Scopes "Policy.ReadWrite.ConditionalAccess"

$params = @{
    DisplayName = "Require MFA for Admins"
    State = "enabled"
    Conditions = @{
        Users = @{
            IncludeRoles = @("62e90394-69f5-4237-9190-012177145e10")  # Global Administrator
        }
        Applications = @{
            IncludeApplications = @("All")
        }
    }
    GrantControls = @{
        Operator = "OR"
        BuiltInControls = @("mfa")
    }
}
New-MgIdentityConditionalAccessPolicy -BodyParameter $params

1.5 Assign Azure RBAC Roles

Portal

  1. 1. Navigate to resource, resource group, or subscription
  2. 2. Access control (IAM) → Add → Add role assignment
  3. 3. Select role (e.g., Virtual Machine Contributor)
  4. 4. Assign access to: User, group, service principal, or managed identity
  5. 5. Review + assign

Azure CLI

# Assign Contributor at resource group scope
az role assignment create \
  --assignee "jane.admin@contoso.com" \
  --role "Contributor" \
  --resource-group rg-prod

# Assign Reader at subscription scope
az role assignment create \
  --assignee "audit-team@contoso.com" \
  --role "Reader" \
  --scope "/subscriptions/<subscription-id>"

# List role assignments for a resource group
az role assignment list --resource-group rg-prod -o table

# Remove assignment
az role assignment delete --assignee "jane.admin@contoso.com" --role "Contributor" --resource-group rg-prod

PowerShell

# Assign Storage Blob Data Contributor to managed identity
$mi = Get-AzADServicePrincipal -DisplayName "app-storage-reader"
New-AzRoleAssignment `
  -ObjectId $mi.Id `
  -RoleDefinitionName "Storage Blob Data Contributor" `
  -Scope "/subscriptions/<sub-id>/resourceGroups/rg-data/providers/Microsoft.Storage/storageAccounts/stcontoso001"

1.6 Create Custom RBAC Role

Azure CLI

# Export existing role as template
az role definition list --name "Virtual Machine Contributor" > vm-contrib-role.json

# Create custom role (example: start/stop VMs only)
cat > custom-vm-operator.json << 'EOF'
{
  "Name": "VM Operator",
  "IsCustom": true,
  "Description": "Can start and stop VMs only",
  "Actions": [
    "Microsoft.Compute/virtualMachines/start/action",
    "Microsoft.Compute/virtualMachines/restart/action",
    "Microsoft.Compute/virtualMachines/deallocate/action",
    "Microsoft.Compute/virtualMachines/read"
  ],
  "NotActions": [],
  "AssignableScopes": ["/subscriptions/<subscription-id>"]
}
EOF

az role definition create --role-definition custom-vm-operator.json

1.7 Configure Management Groups

Azure CLI

# Create hierarchy
az account management-group create --name mg-root --display-name "Contoso Root"
az account management-group create --name mg-prod --display-name "Production" --parent mg-root
az account management-group create --name mg-dev --display-name "Development" --parent mg-root

# Move subscription
az account management-group subscription add --name mg-prod --subscription "<sub-id>"

# View tree
az account management-group list --tree

PowerShell

New-AzManagementGroup -GroupName "mg-prod" -DisplayName "Production"
New-AzManagementGroupSubscription -GroupName "mg-prod" -SubscriptionId "<sub-id>"
Get-AzManagementGroup -GroupName "mg-prod" -Expand

1.8 Assign Azure Policy

Portal

  1. 1. Policy → Assignments → Assign policy
  2. 2. Scope: Management group, subscription, or resource group
  3. 3. Select policy definition (e.g., "Storage accounts should restrict network access")
  4. 4. Set parameters; choose Managed Identity for DeployIfNotExists policies
  5. 5. Review + create

Azure CLI

# Assign built-in policy — require tag on resource groups
az policy assignment create \
  --name "require-env-tag" \
  --display-name "Require Environment tag on RGs" \
  --policy "871b6d14-10aa-478d-b590-943f16f122d3" \
  --scope "/subscriptions/<sub-id>" \
  --params '{"tagName":{"value":"Environment"}}'

# Check compliance
az policy state summarize --resource "/subscriptions/<sub-id>"

# List non-compliant resources
az policy state list --filter "ComplianceState eq 'NonCompliant'" -o table

1.9 Apply Resource Locks

# CanNotDelete lock on production resource group
az lock create --name CanNotDelete-lock --lock-type CanNotDelete \
  --resource-group rg-prod

# ReadOnly lock on critical storage account
az lock create --name ReadOnly-lock --lock-type ReadOnly \
  --resource-group rg-data \
  --resource-name stcontoso001 \
  --resource-type Microsoft.Storage/storageAccounts

# List locks
az lock list --resource-group rg-prod -o table

# Remove lock
az lock delete --name CanNotDelete-lock --resource-group rg-prod

1.10 Deploy Resources with Bicep

File: main.bicep

targetScope = 'resourceGroup'

@description('Environment tag value')
param environment string = 'dev'

@description('Azure region')
param location string = resourceGroup().location

resource storage 'Microsoft.Storage/storageAccounts@2023-01-01' = {
  name: 'st${uniqueString(resourceGroup().id)}'
  location: location
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'StorageV2'
  tags: {
    Environment: environment
  }
  properties: {
    supportsHttpsTrafficOnly: true
    minimumTlsVersion: 'TLS1_2'
    allowBlobPublicAccess: false
  }
}

output storageAccountName string = storage.name

Deploy

az deployment group create \
  --resource-group rg-iac \
  --template-file main.bicep \
  --parameters environment=prod

# What-if (preview changes)
az deployment group what-if \
  --resource-group rg-iac \
  --template-file main.bicep

1.11 Configure Privileged Identity Management (PIM)

Portal (requires Entra ID P2)

  1. 1. Entra ID → Privileged Identity Management → Microsoft Entra roles
  2. 2. Assignments → Add assignments → Select role (e.g., Global Administrator)
  3. 3. Assignment type: Eligible (not Active)
  4. 4. Select member, set duration
  5. 5. Settings → Role settings → Configure activation requirements (MFA, approval, max duration)

DOMAIN 2: IMPLEMENT AND MANAGE STORAGE (15–20%)

2.1 Create Storage Account

az storage account create \
  --resource-group rg-storage \
  --name stcontoso001 \
  --location westeurope \
  --sku Standard_GZRS \
  --kind StorageV2 \
  --https-only true \
  --min-tls-version TLS1_2 \
  --allow-blob-public-access false \
  --tags Environment=Production Owner=StorageTeam

# Enable infrastructure encryption
az storage account update \
  --name stcontoso001 \
  --resource-group rg-storage \
  --require-infrastructure-encryption true

PowerShell

New-AzStorageAccount `
  -ResourceGroupName rg-storage `
  -Name stcontoso001 `
  -Location westeurope `
  -SkuName Standard_GZRS `
  -Kind StorageV2 `
  -EnableHttpsTrafficOnly $true `
  -MinimumTlsVersion TLS1_2 `
  -AllowBlobPublicAccess $false

2.2 Configure Storage Firewall and Private Endpoint

Firewall — selected networks

az storage account network-rule add \
  --account-name stcontoso001 \
  --resource-group rg-storage \
  --subnet /subscriptions/<sub-id>/resourceGroups/rg-net/providers/Microsoft.Network/virtualNetworks/vnet-prod/subnets/snet-app

# Set default action to Deny
az storage account update \
  --name stcontoso001 \
  --resource-group rg-storage \
  --default-action Deny

Private Endpoint

# Create private endpoint
az network private-endpoint create \
  --name pe-storage-blob \
  --resource-group rg-storage \
  --vnet-name vnet-prod \
  --subnet snet-private-endpoints \
  --private-connection-resource-id /subscriptions/<sub-id>/resourceGroups/rg-storage/providers/Microsoft.Storage/storageAccounts/stcontoso001 \
  --group-id blob \
  --connection-name storage-blob-connection

# Create and link Private DNS Zone
az network private-dns zone create \
  --resource-group rg-storage \
  --name privatelink.blob.core.windows.net

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

# Create DNS zone group on private endpoint
az network private-endpoint dns-zone-group create \
  --resource-group rg-storage \
  --endpoint-name pe-storage-blob \
  --name default \
  --private-dns-zone privatelink.blob.core.windows.net \
  --zone-name blob

2.3 Create and Manage Blob Containers

# Get connection string (admin task — prefer MI in production)
az storage account show-connection-string -n stcontoso001 -g rg-storage

# Create container
az storage container create \
  --name data \
  --account-name stcontoso001 \
  --auth-mode login \
  --public-access off

# Upload blob
az storage blob upload \
  --account-name stcontoso001 \
  --container-name data \
  --name report.pdf \
  --file ./report.pdf \
  --auth-mode login

# Set blob tier
az storage blob set-tier \
  --account-name stcontoso001 \
  --container-name data \
  --name archive-data.csv \
  --tier Cool \
  --auth-mode login

Enable Blob Soft Delete

az storage blob service-properties delete-policy update \
  --account-name stcontoso001 \
  --enable true \
  --days-retained 14 \
  --auth-mode login

2.4 Generate and Manage SAS Tokens

# User delegation SAS (preferred — uses Entra ID)
az storage blob generate-sas \
  --account-name stcontoso001 \
  --container-name data \
  --name report.pdf \
  --permissions r \
  --expiry 2026-12-31T23:59Z \
  --auth-mode login \
  --as-user \
  --full-uri

# Stored access policy (create policy first, then reference in SAS)
az storage container policy create \
  --account-name stcontoso001 \
  --container-name data \
  --name readonly-policy \
  --permissions r \
  --expiry 2026-12-31T23:59Z \
  --auth-mode login

2.5 Create Azure File Share

# Create share
az storage share create \
  --name teamdata \
  --account-name stcontoso001 \
  --quota 100 \
  --auth-mode login

# Generate SAS for file share
az storage share generate-sas \
  --account-name stcontoso001 \
  --name teamdata \
  --permissions rwdl \
  --expiry 2026-12-31 \
  --auth-mode login

Mount on Windows VM

# On Windows VM — mount Azure File share
$connectTestResult = Test-NetConnection -ComputerName stcontoso001.file.core.windows.net -Port 445
if ($connectTestResult.TcpTestSucceeded) {
    cmd.exe /C "cmdkey /add:`"stcontoso001.file.core.windows.net`" /user:`"localhost\stcontoso001`" /pass:`"<storage-account-key>`""
    New-PSDrive -Name Z -PSProvider FileSystem -Root "\\stcontoso001.file.core.windows.net\teamdata" -Persist
}

2.6 Configure Lifecycle Management

# Create lifecycle policy JSON
cat > lifecycle-policy.json << 'EOF'
{
  "rules": [
    {
      "enabled": true,
      "name": "move-to-cool",
      "type": "Lifecycle",
      "definition": {
        "actions": {
          "baseBlob": {
            "tierToCool": { "daysAfterModificationGreaterThan": 30 }
          }
        },
        "filters": {
          "blobTypes": ["blockBlob"],
          "prefixMatch": ["logs/"]
        }
      }
    }
  ]
}
EOF

az storage account management-policy create \
  --account-name stcontoso001 \
  --resource-group rg-storage \
  --policy @lifecycle-policy.json

2.7 Configure Azure Backup for VM

# Create Recovery Services vault
az backup vault create \
  --resource-group rg-backup \
  --name rsv-backup-01 \
  --location westeurope

# Create backup policy
az backup policy create \
  --resource-group rg-backup \
  --vault-name rsv-backup-01 \
  --name DailyBackupPolicy \
  --policy '{
    "schedulePolicy": {
      "schedulePolicyType": "SimpleSchedulePolicy",
      "scheduleRunFrequency": "Daily",
      "scheduleRunTimes": ["2026-01-01T02:00:00"]
    },
    "retentionPolicy": {
      "dailySchedule": { "retentionTimes": ["2026-01-01T02:00:00"], "retentionDuration": { "count": 30, "durationType": "Days" } },
      "retentionPolicyType": "LongTermRetentionPolicy"
    }
  }'

# Enable VM backup
az backup protection enable-for-vm \
  --resource-group rg-backup \
  --vault-name rsv-backup-01 \
  --vm vm-web-01 \
  --policy-name DailyBackupPolicy

DOMAIN 3: DEPLOY AND MANAGE COMPUTE RESOURCES (20–25%)

3.1 Create Linux VM with VNet Integration

# Create VNet and subnet
az network vnet create \
  -g rg-compute -n vnet-prod \
  --address-prefix 10.0.0.0/16 \
  --subnet-name snet-web --subnet-prefix 10.0.1.0/24

# Create NSG
az network nsg create -g rg-compute -n nsg-web
az network nsg rule create -g rg-compute --nsg-name nsg-web \
  -n Allow-SSH --priority 100 --direction Inbound \
  --access Allow --protocol Tcp --destination-port-ranges 22 \
  --source-address-prefixes VirtualNetwork

# Create VM (no public IP — use Bastion for access)
az vm create \
  -g rg-compute -n vm-web-01 \
  --image Ubuntu2204 \
  --size Standard_D2s_v5 \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_rsa.pub \
  --vnet-name vnet-prod --subnet snet-web \
  --nsg nsg-web \
  --public-ip-address "" \
  --zone 1

# Install web server
az vm run-command invoke -g rg-compute -n vm-web-01 \
  --command-id RunShellScript \
  --scripts "sudo apt update && sudo apt install -y nginx"

3.2 Create Windows VM with Availability Set

# Create availability set
az vm availability-set create \
  -g rg-compute -n avset-web \
  --platform-fault-domain-count 2 \
  --platform-update-domain-count 5

# Create Windows VM in availability set
az vm create \
  -g rg-compute -n vm-app-01 \
  --image Win2022Datacenter \
  --size Standard_D4s_v5 \
  --admin-username azureadmin \
  --admin-password '<ComplexP@ssw0rd!>' \
  --vnet-name vnet-prod --subnet snet-app \
  --availability-set avset-web \
  --data-disk-sizes-gb 128

3.3 Resize, Start, Stop, and Deallocate VM

# Stop and deallocate (stops billing for compute)
az vm deallocate -g rg-compute -n vm-web-01

# Start
az vm start -g rg-compute -n vm-web-01

# Resize (must be deallocated for some sizes)
az vm deallocate -g rg-compute -n vm-web-01
az vm resize -g rg-compute -n vm-web-01 --size Standard_D4s_v5
az vm start -g rg-compute -n vm-web-01

# Add data disk
az vm disk attach -g rg-compute --vm-name vm-web-01 \
  --disk vm-web-01-data1 --new --size-gb 256 --sku Premium_LRS

3.4 Create VM Scale Set with Autoscale

az vmss create \
  -g rg-compute -n vmss-web \
  --image Ubuntu2204 \
  --upgrade-policy-mode automatic \
  --instance-count 2 \
  --vnet-name vnet-prod --subnet snet-web \
  --load-balancer lb-web \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_rsa.pub

# Autoscale rule
az monitor autoscale create \
  --resource-group rg-compute \
  --resource vmss-web \
  --resource-type Microsoft.Compute/virtualMachineScaleSets \
  --name autoscale-cpu \
  --min-count 2 --max-count 10 --count 2

az monitor autoscale rule create \
  --resource-group rg-compute \
  --autoscale-name autoscale-cpu \
  --condition "Percentage CPU > 70 avg 5m" \
  --scale out 2

az monitor autoscale rule create \
  --resource-group rg-compute \
  --autoscale-name autoscale-cpu \
  --condition "Percentage CPU < 30 avg 5m" \
  --scale in 1

3.5 Deploy Azure App Service

# Create App Service Plan
az appservice plan create \
  -g rg-app -n asp-contoso \
  --sku S1 --is-linux

# Create Web App
az webapp create \
  -g rg-app -p asp-contoso \
  -n app-contoso-unique

# Deploy from local Git
az webapp deployment source config-local-git -g rg-app -n app-contoso-unique

# Configure custom domain and managed certificate
az webapp config hostname add -g rg-app --webapp-name app-contoso-unique \
  --hostname www.contoso.com

# Enable system-assigned managed identity
az webapp identity assign -g rg-app -n app-contoso-unique

# Create deployment slot
az webapp deployment slot create -g rg-app -n app-contoso-unique --slot staging
az webapp deployment slot swap -g rg-app -n app-contoso-unique --slot staging

3.6 Deploy Azure Container Apps

# Create Container Apps environment
az containerapp env create \
  -g rg-aca -n aca-env \
  -l westeurope

# Deploy container app with scale-to-zero
az containerapp create \
  -g rg-aca -n api-service \
  --environment aca-env \
  --image mcr.microsoft.com/azuredocs/containerapps-helloworld:latest \
  --target-port 80 \
  --ingress external \
  --min-replicas 0 \
  --max-replicas 10 \
  --cpu 0.5 --memory 1.0Gi

3.7 Create AKS Cluster (Admin Basics)

az aks create \
  -g rg-aks -n aks-prod \
  --node-count 3 \
  --node-vm-size Standard_D2s_v5 \
  --enable-managed-identity \
  --network-plugin azure \
  --generate-ssh-keys

az aks get-credentials -g rg-aks -n aks-prod
kubectl get nodes
kubectl get pods --all-namespaces

DOMAIN 4: IMPLEMENT AND MANAGE VIRTUAL NETWORKING (15–20%)

4.1 Create VNet with Subnets and NSG

az network vnet create \
  -g rg-net -n vnet-hub \
  --address-prefix 10.0.0.0/16 \
  --subnet-name AzureFirewallSubnet --subnet-prefix 10.0.1.0/26

az network vnet subnet create \
  -g rg-net --vnet-name vnet-hub \
  -n GatewaySubnet --address-prefix 10.0.2.0/27

az network vnet subnet create \
  -g rg-net --vnet-name vnet-hub \
  -n AzureBastionSubnet --address-prefix 10.0.3.0/26

4.2 Configure VNet Peering

# Hub to spoke
az network vnet peering create \
  -g rg-net -n hub-to-spoke1 \
  --vnet-name vnet-hub \
  --remote-vnet vnet-spoke1 \
  --allow-vnet-access \
  --allow-forwarded-traffic \
  --allow-gateway-transit

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

4.3 Deploy Azure Bastion

# Public IP for Bastion (Standard SKU required)
az network public-ip create \
  -g rg-net -n pip-bastion \
  --sku Standard --zone 1 2 3

# Create Bastion host
az network bastion create \
  -g rg-net -n bastion-prod \
  --public-ip-address pip-bastion \
  --vnet-name vnet-hub \
  --location westeurope \
  --sku Standard

Connect to VM via Bastion (Portal)

  1. 1. VM → Connect → Bastion
  2. 2. Enter username and password/SSH key
  3. 3. Session opens in browser (no public IP on VM required)

4.4 Create Site-to-Site VPN

# Local network gateway (represents on-premises)
az network local-gateway create \
  -g rg-net -n lgw-onprem \
  --gateway-ip-address 203.0.113.10 \
  --local-address-prefixes 192.168.0.0/16

# VPN Gateway (takes 30-45 minutes)
az network public-ip create -g rg-net -n pip-vpngw --sku Standard
az network vnet subnet create -g rg-net --vnet-name vnet-hub \
  -n GatewaySubnet --address-prefix 10.0.2.0/27

az network vnet-gateway create \
  -g rg-net -n vpngw-hub \
  --public-ip-address pip-vpngw \
  --vnet vnet-hub \
  --gateway-type Vpn \
  --vpn-type RouteBased \
  --sku VpnGw1

# Create connection
az network vpn-connection create \
  -g rg-net -n conn-onprem \
  --vnet-gateway1 vpngw-hub \
  --local-gateway2 lgw-onprem \
  --shared-key '<PreSharedKey123!>'

4.5 Create Load Balancer

# Public IP
az network public-ip create -g rg-net -n pip-lb --sku Standard

# Load balancer
az network lb create -g rg-net -n lb-web \
  --sku Standard --public-ip-address pip-lb \
  --frontend-ip-name fe-lb --backend-pool-name be-pool

# Health probe
az network lb probe create -g rg-net --lb-name lb-web \
  -n probe-http --protocol Http --port 80 --path /

# Load balancing rule
az network lb rule create -g rg-net --lb-name lb-web \
  -n rule-http --protocol Tcp --frontend-port 80 --backend-port 80 \
  --frontend-ip-name fe-lb --backend-pool-name be-pool --probe-name probe-http

# Add VM to backend pool
az network nic ip-config address-pool add \
  -g rg-compute --nic-name vm-web-01VMNic \
  --ip-config-name ipconfigvm-web-01 \
  --lb-name lb-web --address-pool be-pool

4.6 Configure Network Watcher

# Enable Network Watcher for region
az network watcher configure --locations westeurope --enabled true

# Enable NSG flow logs
az network watcher flow-log create \
  --resource-group rg-net \
  --name nsg-flow-log \
  --nsg nsg-web \
  --enabled true \
  --storage-account stcontoso001 \
  --format JSON \
  --log-version 2 \
  --retention 30

# Connection troubleshoot
az network watcher test-connectivity \
  --source-resource vm-web-01 \
  --dest-resource vm-app-01 \
  --dest-port 443

DOMAIN 5: MONITOR AND MAINTAIN AZURE RESOURCES (10–15%)

5.1 Create Log Analytics Workspace

az monitor log-analytics workspace create \
  -g rg-monitor -n law-central \
  --location westeurope \
  --retention-time 90

# Get workspace ID for diagnostic settings
az monitor log-analytics workspace show \
  -g rg-monitor -n law-central \
  --query id -o tsv

5.2 Configure Diagnostic Settings

# VM diagnostic settings
az monitor diagnostic-settings create \
  --name vm-diag \
  --resource $(az vm show -g rg-compute -n vm-web-01 --query id -o tsv) \
  --workspace law-central \
  --metrics '[{"category":"AllMetrics","enabled":true,"retentionPolicy":{"enabled":false,"days":0}}]' \
  --logs '[
    {"category":"Syslog","enabled":true},
    {"category":"Audit","enabled":true}
  ]'

# Storage account diagnostic settings
az monitor diagnostic-settings create \
  --name storage-diag \
  --resource $(az storage account show -g rg-storage -n stcontoso001 --query id -o tsv) \
  --workspace law-central \
  --logs '[{"category":"StorageRead","enabled":true},{"category":"StorageWrite","enabled":true}]'

5.3 Create Metric Alert

# Create action group
az monitor action-group create \
  -g rg-monitor -n ag-critical \
  --short-name Critical \
  --email admin admin@contoso.com

# Metric alert — CPU > 80%
az monitor metrics alert create \
  -g rg-monitor -n alert-vm-cpu \
  --scopes $(az vm show -g rg-compute -n vm-web-01 --query id -o tsv) \
  --condition "avg Percentage CPU > 80" \
  --window-size 5m \
  --evaluation-frequency 1m \
  --action ag-critical \
  --description "VM CPU exceeded 80%"

5.4 Create Log Alert (KQL)

az monitor scheduled-query create \
  -g rg-monitor -n alert-failed-logins \
  --scopes $(az monitor log-analytics workspace show -g rg-monitor -n law-central --query id -o tsv) \
  --condition-query "SigninLogs | where ResultType != 0 | summarize count() by UserPrincipalName | where count_ > 5" \
  --condition "count > 0" \
  --description "More than 5 failed logins" \
  --evaluation-frequency 5m \
  --window-size 15m \
  --action ag-critical

5.5 Install Azure Monitor Agent

# Create data collection endpoint and rule
az monitor data-collection endpoint create \
  -g rg-monitor -n dce-central -l westeurope

# Install AMA on VM via extension
az vm extension set \
  -g rg-compute --vm-name vm-web-01 \
  --name AzureMonitorLinuxAgent \
  --publisher Microsoft.Azure.Monitor

5.6 Query Logs with KQL

// VM heartbeat — is agent reporting?
Heartbeat
| where TimeGenerated > ago(1h)
| summarize LastHeartbeat = max(TimeGenerated) by Computer
| where LastHeartbeat < ago(15m)

// Failed Azure activity operations
AzureActivity
| where OperationNameValue endswith "DELETE"
| where ActivityStatusValue == "Success"
| project TimeGenerated, Caller, OperationNameValue, ResourceGroup

// NSG denied traffic
AzureNetworkAnalytics_CL
| where FlowStatus_s == "D"  // Denied
| summarize count() by SourceIP_s, DestIP_s, DestPort_d

5.7 Move Resources Between Resource Groups

# Validate move
az resource move --destination-group rg-new \
  --ids $(az vm show -g rg-compute -n vm-web-01 --query id -o tsv) \
  --validate

# Execute move
az resource move --destination-group rg-new \
  --ids $(az vm show -g rg-compute -n vm-web-01 --query id -o tsv)

5.8 Export and Review Activity Log

# Query activity log
az monitor activity-log list \
  --resource-group rg-prod \
  --start-time 2026-07-01 \
  --offset 7d \
  --query "[].{Time:eventTimestamp, Caller:caller, Operation:operationName.value, Status:status.value}" \
  -o table

*Skills measured: April 17, 2026. Practice each procedure in a lab subscription before exam day.*