AZ-104 Hands-on Labs โ
๐งช Overview โ
This section contains 45+ hands-on labs designed to give you practical experience with Azure administration tasks. Each lab includes step-by-step instructions, Azure CLI/PowerShell commands, and verification steps.
Want More Advanced Projects?
Check out our Real-World Projects section for 7 comprehensive enterprise-grade projects with detailed architecture diagrams, including:
- Hub-Spoke Network Architecture
- Hybrid Identity with Azure AD
- Multi-Tier Web Application
- Disaster Recovery Solution
- Infrastructure as Code Pipeline
- Governance Framework
- Private Endpoint Implementation
๐ Prerequisites โ
Before starting the labs, ensure you have:
- Azure Subscription: Free tier or Pay-as-you-go
- Azure CLI: Installed and configured
- Azure PowerShell: Az module installed
- VS Code: With Azure extensions (optional but recommended)
Setting Up Your Environment โ
# Install Azure CLI (macOS)
brew install azure-cli
# Install Azure CLI (Windows)
winget install Microsoft.AzureCLI
# Login to Azure
az login
# Set default subscription
az account set --subscription "Your-Subscription-Name"# Install Azure PowerShell
Install-Module -Name Az -AllowClobber -Scope CurrentUser
# Login to Azure
Connect-AzAccount
# Set default subscription
Set-AzContext -Subscription "Your-Subscription-Name"๐ Domain 1: Identity and Governance Labs โ
Lab 1.1: Create and Manage Microsoft Entra Users โ
Objective: Create users, assign licenses, and manage user properties
Steps:
- Navigate to Microsoft Entra ID in Azure Portal
- Create a new user with the following details:
- User principal name:
testuser1@yourdomain.onmicrosoft.com - Display name: Test User 1
- Password: Auto-generate
- User principal name:
- Create a second user using Azure CLI
# Create user with Azure CLI
az ad user create \
--display-name "Test User 2" \
--user-principal-name "testuser2@yourdomain.onmicrosoft.com" \
--password "ComplexP@ssw0rd!" \
--force-change-password-next-sign-in trueVerification:
- List all users:
az ad user list --output table
Lab 1.2: Create and Manage Groups โ
Objective: Create security and Microsoft 365 groups
Steps:
- Create a security group in the portal
- Create a dynamic group based on user attributes
- Add members to groups
# Create security group
az ad group create \
--display-name "AZ104-Admins" \
--mail-nickname "az104admins"
# Add member to group
az ad group member add \
--group "AZ104-Admins" \
--member-id "<user-object-id>"# Create group with PowerShell
New-AzADGroup -DisplayName "AZ104-Developers" -MailNickname "az104devs"Lab 1.3: Configure Self-Service Password Reset (SSPR) โ
Objective: Enable and configure SSPR for users
Steps:
- Navigate to Microsoft Entra ID โ Password reset
- Enable SSPR for selected groups or all users
- Configure authentication methods:
- Mobile phone
- Security questions
- Configure registration settings
- Test SSPR with a test user
Verification:
- Access https://aka.ms/sspr to test password reset
Lab 1.4: Implement Role-Based Access Control (RBAC) โ
Objective: Assign built-in and custom roles
Steps:
- Create a resource group for testing
- Assign Reader role to a user at resource group scope
- Assign Contributor role at subscription scope
- Create a custom role
# Create resource group
az group create --name "RBAC-Lab-RG" --location "eastus"
# Assign Reader role
az role assignment create \
--assignee "testuser1@yourdomain.onmicrosoft.com" \
--role "Reader" \
--resource-group "RBAC-Lab-RG"
# List role assignments
az role assignment list --resource-group "RBAC-Lab-RG" --output tableCustom Role Definition (save as custom-role.json):
{
"Name": "VM Operator",
"Description": "Can start and stop VMs",
"Actions": [
"Microsoft.Compute/virtualMachines/start/action",
"Microsoft.Compute/virtualMachines/powerOff/action",
"Microsoft.Compute/virtualMachines/restart/action",
"Microsoft.Compute/virtualMachines/read"
],
"AssignableScopes": ["/subscriptions/<subscription-id>"]
}# Create custom role
az role definition create --role-definition custom-role.jsonLab 1.5: Create and Assign Azure Policies โ
Objective: Create policy definitions and assignments
Steps:
- Create a policy that requires tags on resources
- Assign the policy to a resource group
- Test policy compliance
# Assign built-in policy (Require tag on resources)
az policy assignment create \
--name "require-department-tag" \
--display-name "Require Department Tag" \
--policy "/providers/Microsoft.Authorization/policyDefinitions/871b6d14-10aa-478d-b590-94f262ecfa99" \
--scope "/subscriptions/<subscription-id>/resourceGroups/RBAC-Lab-RG" \
--params '{"tagName": {"value": "Department"}}'
# Check compliance
az policy state list --resource-group "RBAC-Lab-RG" --output tableLab 1.6: Configure Resource Locks โ
Objective: Implement CanNotDelete and ReadOnly locks
Steps:
- Create a storage account
- Apply a CanNotDelete lock
- Try to delete the resource (should fail)
- Apply a ReadOnly lock
- Try to modify the resource (should fail)
# Create storage account
az storage account create \
--name "locklab$(date +%s)" \
--resource-group "RBAC-Lab-RG" \
--sku Standard_LRS
# Create CanNotDelete lock
az lock create \
--name "CannotDeleteLock" \
--lock-type CanNotDelete \
--resource-group "RBAC-Lab-RG" \
--resource-name "<storage-account-name>" \
--resource-type "Microsoft.Storage/storageAccounts"
# List locks
az lock list --resource-group "RBAC-Lab-RG" --output table๐พ Domain 2: Storage Labs โ
Lab 2.1: Create Storage Accounts with Different Redundancy โ
Objective: Create storage accounts with LRS, GRS, and ZRS
# Create LRS storage account
az storage account create \
--name "storagelrs$(date +%s)" \
--resource-group "Storage-Lab-RG" \
--location "eastus" \
--sku Standard_LRS \
--kind StorageV2
# Create GRS storage account
az storage account create \
--name "storagegrs$(date +%s)" \
--resource-group "Storage-Lab-RG" \
--location "eastus" \
--sku Standard_GRS \
--kind StorageV2
# Create ZRS storage account
az storage account create \
--name "storagezrs$(date +%s)" \
--resource-group "Storage-Lab-RG" \
--location "eastus" \
--sku Standard_ZRS \
--kind StorageV2Lab 2.2: Configure Blob Storage and Access Tiers โ
Objective: Create containers, upload blobs, and manage access tiers
# Get storage account key
STORAGE_KEY=$(az storage account keys list \
--account-name "<storage-account>" \
--resource-group "Storage-Lab-RG" \
--query "[0].value" -o tsv)
# Create container
az storage container create \
--name "documents" \
--account-name "<storage-account>" \
--account-key $STORAGE_KEY \
--public-access off
# Upload blob
az storage blob upload \
--account-name "<storage-account>" \
--account-key $STORAGE_KEY \
--container-name "documents" \
--name "sample.txt" \
--file "./sample.txt"
# Change blob tier to Cool
az storage blob set-tier \
--account-name "<storage-account>" \
--account-key $STORAGE_KEY \
--container-name "documents" \
--name "sample.txt" \
--tier CoolLab 2.3: Configure Lifecycle Management โ
Objective: Create lifecycle management policies
# Create lifecycle policy (save as lifecycle-policy.json)
cat > lifecycle-policy.json << 'EOF'
{
"rules": [
{
"enabled": true,
"name": "move-to-cool",
"type": "Lifecycle",
"definition": {
"actions": {
"baseBlob": {
"tierToCool": {
"daysAfterModificationGreaterThan": 30
},
"tierToArchive": {
"daysAfterModificationGreaterThan": 90
},
"delete": {
"daysAfterModificationGreaterThan": 365
}
}
},
"filters": {
"blobTypes": ["blockBlob"],
"prefixMatch": ["documents/"]
}
}
}
]
}
EOF
# Apply lifecycle policy
az storage account management-policy create \
--account-name "<storage-account>" \
--resource-group "Storage-Lab-RG" \
--policy @lifecycle-policy.jsonLab 2.4: Create Azure File Shares โ
Objective: Create and mount Azure file shares
# Create file share
az storage share create \
--name "fileshare01" \
--account-name "<storage-account>" \
--account-key $STORAGE_KEY \
--quota 100
# Get connection script for Windows
az storage account show-connection-string \
--name "<storage-account>" \
--resource-group "Storage-Lab-RG"Mount on Windows:
$connectTestResult = Test-NetConnection -ComputerName <storage-account>.file.core.windows.net -Port 445
if ($connectTestResult.TcpTestSucceeded) {
cmd.exe /C "cmdkey /add:`"<storage-account>.file.core.windows.net`" /user:`"Azure\<storage-account>`" /pass:`"<storage-key>`""
New-PSDrive -Name Z -PSProvider FileSystem -Root "\\<storage-account>.file.core.windows.net\fileshare01" -Persist
}Mount on Linux:
sudo mount -t cifs //<storage-account>.file.core.windows.net/fileshare01 /mnt/fileshare \
-o vers=3.0,username=<storage-account>,password=<storage-key>,dir_mode=0777,file_mode=0777Lab 2.5: Configure SAS Tokens โ
Objective: Generate and use Shared Access Signatures
# Generate account SAS
az storage account generate-sas \
--account-name "<storage-account>" \
--account-key $STORAGE_KEY \
--services b \
--resource-types sco \
--permissions rwdlacup \
--expiry "2025-12-31T00:00:00Z"
# Generate container SAS
az storage container generate-sas \
--account-name "<storage-account>" \
--account-key $STORAGE_KEY \
--name "documents" \
--permissions rl \
--expiry "2025-12-31T00:00:00Z"
# Generate blob SAS
az storage blob generate-sas \
--account-name "<storage-account>" \
--account-key $STORAGE_KEY \
--container-name "documents" \
--name "sample.txt" \
--permissions r \
--expiry "2025-12-31T00:00:00Z"Lab 2.6: Configure Private Endpoints for Storage โ
Objective: Secure storage with private endpoints
# Create VNet and subnet
az network vnet create \
--name "StorageVNet" \
--resource-group "Storage-Lab-RG" \
--address-prefix "10.0.0.0/16" \
--subnet-name "PrivateEndpointSubnet" \
--subnet-prefix "10.0.1.0/24"
# Disable subnet private endpoint policies
az network vnet subnet update \
--name "PrivateEndpointSubnet" \
--vnet-name "StorageVNet" \
--resource-group "Storage-Lab-RG" \
--disable-private-endpoint-network-policies true
# Create private endpoint
az network private-endpoint create \
--name "StoragePrivateEndpoint" \
--resource-group "Storage-Lab-RG" \
--vnet-name "StorageVNet" \
--subnet "PrivateEndpointSubnet" \
--private-connection-resource-id "<storage-account-resource-id>" \
--group-id "blob" \
--connection-name "StorageBlobConnection"๐ฅ๏ธ Domain 3: Compute Labs โ
Lab 3.1: Create Virtual Machines โ
Objective: Create Windows and Linux VMs
# Create resource group
az group create --name "Compute-Lab-RG" --location "eastus"
# Create Linux VM
az vm create \
--resource-group "Compute-Lab-RG" \
--name "LinuxVM01" \
--image Ubuntu2204 \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys
# Create Windows VM
az vm create \
--resource-group "Compute-Lab-RG" \
--name "WindowsVM01" \
--image Win2022Datacenter \
--size Standard_B2s \
--admin-username azureuser \
--admin-password "ComplexP@ssw0rd!"Lab 3.2: Configure Availability Sets โ
Objective: Create VMs in an availability set
# Create availability set
az vm availability-set create \
--name "WebAvailSet" \
--resource-group "Compute-Lab-RG" \
--platform-fault-domain-count 2 \
--platform-update-domain-count 5
# Create VMs in availability set
for i in 1 2; do
az vm create \
--resource-group "Compute-Lab-RG" \
--name "WebVM0$i" \
--availability-set "WebAvailSet" \
--image Ubuntu2204 \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys \
--no-wait
doneLab 3.3: Create VM Scale Sets โ
Objective: Create and configure VM Scale Sets with autoscaling
# Create VMSS
az vmss create \
--resource-group "Compute-Lab-RG" \
--name "WebVMSS" \
--image Ubuntu2204 \
--instance-count 2 \
--vm-sku Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys \
--upgrade-policy-mode Automatic
# Configure autoscale
az monitor autoscale create \
--resource-group "Compute-Lab-RG" \
--resource "WebVMSS" \
--resource-type Microsoft.Compute/virtualMachineScaleSets \
--name "WebVMSS-Autoscale" \
--min-count 2 \
--max-count 10 \
--count 2
# Add scale-out rule (CPU > 70%)
az monitor autoscale rule create \
--resource-group "Compute-Lab-RG" \
--autoscale-name "WebVMSS-Autoscale" \
--condition "Percentage CPU > 70 avg 5m" \
--scale out 1
# Add scale-in rule (CPU < 30%)
az monitor autoscale rule create \
--resource-group "Compute-Lab-RG" \
--autoscale-name "WebVMSS-Autoscale" \
--condition "Percentage CPU < 30 avg 5m" \
--scale in 1Lab 3.4: Deploy with ARM Templates โ
Objective: Create and deploy ARM templates
ARM Template (save as azuredeploy.json):
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageAccountName": {
"type": "string",
"metadata": {
"description": "Name of the storage account"
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]"
}
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-09-01",
"name": "[parameters('storageAccountName')]",
"location": "[parameters('location')]",
"sku": {
"name": "Standard_LRS"
},
"kind": "StorageV2",
"properties": {
"supportsHttpsTrafficOnly": true,
"minimumTlsVersion": "TLS1_2"
}
}
],
"outputs": {
"storageAccountId": {
"type": "string",
"value": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]"
}
}
}# Deploy ARM template
az deployment group create \
--resource-group "Compute-Lab-RG" \
--template-file azuredeploy.json \
--parameters storageAccountName="armlab$(date +%s)"
# What-if deployment
az deployment group what-if \
--resource-group "Compute-Lab-RG" \
--template-file azuredeploy.json \
--parameters storageAccountName="armlab$(date +%s)"Lab 3.5: Deploy with Bicep โ
Objective: Create and deploy Bicep templates
Bicep Template (save as main.bicep):
@description('Name of the storage account')
param storageAccountName string
@description('Location for resources')
param location string = resourceGroup().location
@allowed([
'Standard_LRS'
'Standard_GRS'
'Standard_ZRS'
])
param skuName string = 'Standard_LRS'
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = {
name: storageAccountName
location: location
sku: {
name: skuName
}
kind: 'StorageV2'
properties: {
supportsHttpsTrafficOnly: true
minimumTlsVersion: 'TLS1_2'
accessTier: 'Hot'
}
}
output storageAccountId string = storageAccount.id
output primaryEndpoints object = storageAccount.properties.primaryEndpoints# Deploy Bicep
az deployment group create \
--resource-group "Compute-Lab-RG" \
--template-file main.bicep \
--parameters storageAccountName="biceplab$(date +%s)"Lab 3.6: Deploy Azure Container Instances โ
Objective: Deploy containers with ACI
# Create container instance
az container create \
--resource-group "Compute-Lab-RG" \
--name "nginx-container" \
--image nginx:latest \
--cpu 1 \
--memory 1.5 \
--ports 80 \
--dns-name-label "nginx-$(date +%s)" \
--ip-address Public
# Get container details
az container show \
--resource-group "Compute-Lab-RG" \
--name "nginx-container" \
--query "{FQDN:ipAddress.fqdn,IP:ipAddress.ip,State:instanceView.state}"Lab 3.7: Create Azure Container Registry โ
Objective: Create ACR and push images
# Create ACR
az acr create \
--resource-group "Compute-Lab-RG" \
--name "acr$(date +%s)" \
--sku Basic \
--admin-enabled true
# Login to ACR
az acr login --name "<acr-name>"
# Tag and push image
docker tag nginx:latest <acr-name>.azurecr.io/nginx:v1
docker push <acr-name>.azurecr.io/nginx:v1
# List images
az acr repository list --name "<acr-name>" --output tableLab 3.8: Create Azure App Service โ
Objective: Deploy a web app to App Service
# Create App Service plan
az appservice plan create \
--name "WebAppPlan" \
--resource-group "Compute-Lab-RG" \
--sku B1 \
--is-linux
# Create web app
az webapp create \
--resource-group "Compute-Lab-RG" \
--plan "WebAppPlan" \
--name "webapp-$(date +%s)" \
--runtime "NODE:18-lts"
# Configure deployment from GitHub
az webapp deployment source config \
--resource-group "Compute-Lab-RG" \
--name "<webapp-name>" \
--repo-url "https://github.com/Azure-Samples/nodejs-docs-hello-world" \
--branch master \
--manual-integrationLab 3.9: Configure Deployment Slots โ
Objective: Create and swap deployment slots
# Create staging slot
az webapp deployment slot create \
--resource-group "Compute-Lab-RG" \
--name "<webapp-name>" \
--slot staging
# Deploy to staging
az webapp deployment source config \
--resource-group "Compute-Lab-RG" \
--name "<webapp-name>" \
--slot staging \
--repo-url "https://github.com/Azure-Samples/nodejs-docs-hello-world" \
--branch develop \
--manual-integration
# Swap slots
az webapp deployment slot swap \
--resource-group "Compute-Lab-RG" \
--name "<webapp-name>" \
--slot staging \
--target-slot production๐ Domain 4: Networking Labs โ
Lab 4.1: Create Virtual Networks โ
Objective: Create VNets with subnets
# Create resource group
az group create --name "Network-Lab-RG" --location "eastus"
# Create VNet
az network vnet create \
--resource-group "Network-Lab-RG" \
--name "MainVNet" \
--address-prefix "10.0.0.0/16" \
--subnet-name "WebSubnet" \
--subnet-prefix "10.0.1.0/24"
# Add additional subnets
az network vnet subnet create \
--resource-group "Network-Lab-RG" \
--vnet-name "MainVNet" \
--name "AppSubnet" \
--address-prefix "10.0.2.0/24"
az network vnet subnet create \
--resource-group "Network-Lab-RG" \
--vnet-name "MainVNet" \
--name "DbSubnet" \
--address-prefix "10.0.3.0/24"Lab 4.2: Configure Network Security Groups โ
Objective: Create and configure NSGs
# Create NSG
az network nsg create \
--resource-group "Network-Lab-RG" \
--name "WebNSG"
# Add inbound rule for HTTP
az network nsg rule create \
--resource-group "Network-Lab-RG" \
--nsg-name "WebNSG" \
--name "AllowHTTP" \
--priority 100 \
--direction Inbound \
--access Allow \
--protocol Tcp \
--destination-port-range 80
# Add inbound rule for HTTPS
az network nsg rule create \
--resource-group "Network-Lab-RG" \
--nsg-name "WebNSG" \
--name "AllowHTTPS" \
--priority 110 \
--direction Inbound \
--access Allow \
--protocol Tcp \
--destination-port-range 443
# Associate NSG with subnet
az network vnet subnet update \
--resource-group "Network-Lab-RG" \
--vnet-name "MainVNet" \
--name "WebSubnet" \
--network-security-group "WebNSG"Lab 4.3: Create Azure DNS Zones โ
Objective: Configure public and private DNS zones
# Create public DNS zone
az network dns zone create \
--resource-group "Network-Lab-RG" \
--name "contoso.com"
# Add A record
az network dns record-set a add-record \
--resource-group "Network-Lab-RG" \
--zone-name "contoso.com" \
--record-set-name "www" \
--ipv4-address "10.0.1.4"
# Add CNAME record
az network dns record-set cname set-record \
--resource-group "Network-Lab-RG" \
--zone-name "contoso.com" \
--record-set-name "blog" \
--cname "www.contoso.com"
# Create private DNS zone
az network private-dns zone create \
--resource-group "Network-Lab-RG" \
--name "private.contoso.com"
# Link private DNS to VNet
az network private-dns link vnet create \
--resource-group "Network-Lab-RG" \
--zone-name "private.contoso.com" \
--name "MainVNetLink" \
--virtual-network "MainVNet" \
--registration-enabled trueLab 4.4: Configure VNet Peering โ
Objective: Peer two virtual networks
# Create second VNet
az network vnet create \
--resource-group "Network-Lab-RG" \
--name "SecondVNet" \
--address-prefix "10.1.0.0/16" \
--subnet-name "DefaultSubnet" \
--subnet-prefix "10.1.1.0/24"
# Create peering from MainVNet to SecondVNet
az network vnet peering create \
--resource-group "Network-Lab-RG" \
--name "MainToSecond" \
--vnet-name "MainVNet" \
--remote-vnet "SecondVNet" \
--allow-vnet-access
# Create peering from SecondVNet to MainVNet
az network vnet peering create \
--resource-group "Network-Lab-RG" \
--name "SecondToMain" \
--vnet-name "SecondVNet" \
--remote-vnet "MainVNet" \
--allow-vnet-access
# Verify peering status
az network vnet peering list \
--resource-group "Network-Lab-RG" \
--vnet-name "MainVNet" \
--output tableLab 4.5: Configure Azure Load Balancer โ
Objective: Create a public load balancer
# Create public IP
az network public-ip create \
--resource-group "Network-Lab-RG" \
--name "LBPublicIP" \
--sku Standard \
--allocation-method Static
# Create load balancer
az network lb create \
--resource-group "Network-Lab-RG" \
--name "WebLoadBalancer" \
--sku Standard \
--public-ip-address "LBPublicIP" \
--frontend-ip-name "FrontEnd" \
--backend-pool-name "BackEndPool"
# Create health probe
az network lb probe create \
--resource-group "Network-Lab-RG" \
--lb-name "WebLoadBalancer" \
--name "HealthProbe" \
--protocol Http \
--port 80 \
--path "/"
# Create load balancing rule
az network lb rule create \
--resource-group "Network-Lab-RG" \
--lb-name "WebLoadBalancer" \
--name "HTTPRule" \
--protocol Tcp \
--frontend-port 80 \
--backend-port 80 \
--frontend-ip-name "FrontEnd" \
--backend-pool-name "BackEndPool" \
--probe-name "HealthProbe"Lab 4.6: Configure Application Gateway โ
Objective: Create an Application Gateway with WAF
# Create subnet for App Gateway
az network vnet subnet create \
--resource-group "Network-Lab-RG" \
--vnet-name "MainVNet" \
--name "AppGatewaySubnet" \
--address-prefix "10.0.4.0/24"
# Create public IP for App Gateway
az network public-ip create \
--resource-group "Network-Lab-RG" \
--name "AppGWPublicIP" \
--sku Standard \
--allocation-method Static
# Create Application Gateway
az network application-gateway create \
--resource-group "Network-Lab-RG" \
--name "WebAppGateway" \
--location "eastus" \
--sku WAF_v2 \
--capacity 2 \
--vnet-name "MainVNet" \
--subnet "AppGatewaySubnet" \
--public-ip-address "AppGWPublicIP" \
--http-settings-port 80 \
--http-settings-protocol Http \
--frontend-port 80๐ Domain 5: Monitoring Labs โ
Lab 5.1: Configure Azure Monitor Metrics โ
Objective: View and analyze metrics
# List available metrics for a VM
az monitor metrics list-definitions \
--resource "<vm-resource-id>" \
--output table
# Get CPU metrics
az monitor metrics list \
--resource "<vm-resource-id>" \
--metric "Percentage CPU" \
--interval PT1HLab 5.2: Create Alerts and Action Groups โ
Objective: Configure alerts with notifications
# Create action group
az monitor action-group create \
--resource-group "Monitor-Lab-RG" \
--name "AdminAlerts" \
--short-name "AdminAlrt" \
--action email admin admin@contoso.com
# Create metric alert
az monitor metrics alert create \
--resource-group "Monitor-Lab-RG" \
--name "HighCPUAlert" \
--scopes "<vm-resource-id>" \
--condition "avg Percentage CPU > 80" \
--window-size 5m \
--evaluation-frequency 1m \
--action "AdminAlerts"Lab 5.3: Configure Log Analytics โ
Objective: Create workspace and collect logs
# Create Log Analytics workspace
az monitor log-analytics workspace create \
--resource-group "Monitor-Lab-RG" \
--workspace-name "MainWorkspace"
# Enable VM insights
az vm extension set \
--resource-group "Compute-Lab-RG" \
--vm-name "LinuxVM01" \
--name "AzureMonitorLinuxAgent" \
--publisher "Microsoft.Azure.Monitor" \
--version "1.0"Lab 5.4: Write KQL Queries โ
Objective: Query logs with Kusto Query Language
Sample KQL Queries:
// Get all heartbeats in last hour
Heartbeat
| where TimeGenerated > ago(1h)
| summarize count() by Computer
// Get failed logins
SecurityEvent
| where EventID == 4625
| summarize FailedLogins = count() by Account, Computer
| order by FailedLogins desc
// Get VM performance data
Perf
| where ObjectName == "Processor" and CounterName == "% Processor Time"
| summarize AvgCPU = avg(CounterValue) by Computer, bin(TimeGenerated, 1h)
| render timechart
// Get storage account operations
StorageBlobLogs
| where OperationName == "GetBlob"
| summarize count() by CallerIpAddress
| order by count_ descLab 5.5: Configure Azure Backup โ
Objective: Back up VMs with Recovery Services vault
# Create Recovery Services vault
az backup vault create \
--resource-group "Monitor-Lab-RG" \
--name "MainBackupVault" \
--location "eastus"
# Enable backup for VM
az backup protection enable-for-vm \
--resource-group "Monitor-Lab-RG" \
--vault-name "MainBackupVault" \
--vm "LinuxVM01" \
--policy-name "DefaultPolicy"
# Trigger backup
az backup protection backup-now \
--resource-group "Monitor-Lab-RG" \
--vault-name "MainBackupVault" \
--container-name "<container-name>" \
--item-name "LinuxVM01" \
--retain-until "2025-12-31"Lab 5.6: Configure Azure Site Recovery โ
Objective: Set up disaster recovery for VMs
- Navigate to Recovery Services vault in Azure Portal
- Click "Site Recovery" โ "Enable replication"
- Select source VMs and target region
- Configure replication settings
- Enable replication
- Test failover to verify DR setup
๐งน Cleanup โ
After completing the labs, clean up resources to avoid charges:
# Delete all lab resource groups
az group delete --name "RBAC-Lab-RG" --yes --no-wait
az group delete --name "Storage-Lab-RG" --yes --no-wait
az group delete --name "Compute-Lab-RG" --yes --no-wait
az group delete --name "Network-Lab-RG" --yes --no-wait
az group delete --name "Monitor-Lab-RG" --yes --no-wait