← Back to Portal
File:
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. Microsoft Entra admin center → Users → New user → Create user
- 2. Set User principal name (UPN), display name, password (auto-generate or manual)
- 3. Assign groups and roles as needed
- 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. Entra ID → Password reset → Properties → Enabled: All
- 2. Authentication methods → select 2 methods (mobile phone, email, security questions)
- 3. Registration → Require users to register when signing in: Yes
- 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. Entra ID → Security → Conditional Access → New policy
- 2. Name:
Require MFA for All Users - 3. Users: All users; Exclude: Break-glass emergency accounts
- 4. Cloud apps: All cloud apps
- 5. Grant: Require multifactor authentication
- 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. Navigate to resource, resource group, or subscription
- 2. Access control (IAM) → Add → Add role assignment
- 3. Select role (e.g., Virtual Machine Contributor)
- 4. Assign access to: User, group, service principal, or managed identity
- 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. Policy → Assignments → Assign policy
- 2. Scope: Management group, subscription, or resource group
- 3. Select policy definition (e.g., "Storage accounts should restrict network access")
- 4. Set parameters; choose Managed Identity for DeployIfNotExists policies
- 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. Entra ID → Privileged Identity Management → Microsoft Entra roles
- 2. Assignments → Add assignments → Select role (e.g., Global Administrator)
- 3. Assignment type: Eligible (not Active)
- 4. Select member, set duration
- 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. VM → Connect → Bastion
- 2. Enter username and password/SSH key
- 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.*