Skip to content

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 โ€‹

bash
# 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"
powershell
# 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:

  1. Navigate to Microsoft Entra ID in Azure Portal
  2. Create a new user with the following details:
    • User principal name: testuser1@yourdomain.onmicrosoft.com
    • Display name: Test User 1
    • Password: Auto-generate
  3. Create a second user using Azure CLI
bash
# 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 true

Verification:

  • List all users: az ad user list --output table

Lab 1.2: Create and Manage Groups โ€‹

Objective: Create security and Microsoft 365 groups

Steps:

  1. Create a security group in the portal
  2. Create a dynamic group based on user attributes
  3. Add members to groups
bash
# 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>"
powershell
# 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:

  1. Navigate to Microsoft Entra ID โ†’ Password reset
  2. Enable SSPR for selected groups or all users
  3. Configure authentication methods:
    • Mobile phone
    • Email
    • Security questions
  4. Configure registration settings
  5. Test SSPR with a test user

Verification:


Lab 1.4: Implement Role-Based Access Control (RBAC) โ€‹

Objective: Assign built-in and custom roles

Steps:

  1. Create a resource group for testing
  2. Assign Reader role to a user at resource group scope
  3. Assign Contributor role at subscription scope
  4. Create a custom role
bash
# 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 table

Custom Role Definition (save as custom-role.json):

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>"]
}
bash
# Create custom role
az role definition create --role-definition custom-role.json

Lab 1.5: Create and Assign Azure Policies โ€‹

Objective: Create policy definitions and assignments

Steps:

  1. Create a policy that requires tags on resources
  2. Assign the policy to a resource group
  3. Test policy compliance
bash
# 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 table

Lab 1.6: Configure Resource Locks โ€‹

Objective: Implement CanNotDelete and ReadOnly locks

Steps:

  1. Create a storage account
  2. Apply a CanNotDelete lock
  3. Try to delete the resource (should fail)
  4. Apply a ReadOnly lock
  5. Try to modify the resource (should fail)
bash
# 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

bash
# 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 StorageV2

Lab 2.2: Configure Blob Storage and Access Tiers โ€‹

Objective: Create containers, upload blobs, and manage access tiers

bash
# 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 Cool

Lab 2.3: Configure Lifecycle Management โ€‹

Objective: Create lifecycle management policies

bash
# 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.json

Lab 2.4: Create Azure File Shares โ€‹

Objective: Create and mount Azure file shares

bash
# 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:

powershell
$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:

bash
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=0777

Lab 2.5: Configure SAS Tokens โ€‹

Objective: Generate and use Shared Access Signatures

bash
# 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

bash
# 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

bash
# 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

bash
# 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
done

Lab 3.3: Create VM Scale Sets โ€‹

Objective: Create and configure VM Scale Sets with autoscaling

bash
# 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 1

Lab 3.4: Deploy with ARM Templates โ€‹

Objective: Create and deploy ARM templates

ARM Template (save as azuredeploy.json):

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'))]"
    }
  }
}
bash
# 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):

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
bash
# 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

bash
# 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

bash
# 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 table

Lab 3.8: Create Azure App Service โ€‹

Objective: Deploy a web app to App Service

bash
# 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-integration

Lab 3.9: Configure Deployment Slots โ€‹

Objective: Create and swap deployment slots

bash
# 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

bash
# 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

bash
# 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

bash
# 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 true

Lab 4.4: Configure VNet Peering โ€‹

Objective: Peer two virtual networks

bash
# 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 table

Lab 4.5: Configure Azure Load Balancer โ€‹

Objective: Create a public load balancer

bash
# 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

bash
# 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

bash
# 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 PT1H

Lab 5.2: Create Alerts and Action Groups โ€‹

Objective: Configure alerts with notifications

bash
# 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

bash
# 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:

kusto
// 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_ desc

Lab 5.5: Configure Azure Backup โ€‹

Objective: Back up VMs with Recovery Services vault

bash
# 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

  1. Navigate to Recovery Services vault in Azure Portal
  2. Click "Site Recovery" โ†’ "Enable replication"
  3. Select source VMs and target region
  4. Configure replication settings
  5. Enable replication
  6. Test failover to verify DR setup

๐Ÿงน Cleanup โ€‹

After completing the labs, clean up resources to avoid charges:

bash
# 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

๐Ÿ“š Additional Resources โ€‹

Released under the MIT License.