Lab 1: Azure Local Deployment
π§ Lab Under Development
This lab content is complete but hands-on exercises are currently being validated and refined.
Expected Release: Q1 2026
You can review the lab steps and prepare your environment in advance.
Objective
Deploy and configure Azure Local in connected mode, including networking, storage, and a sample workload. This lab simulates the deployment process and validates connectivity.
Pre-Lab Checklist
PREREQUISITES
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Required:
β Azure subscription with Owner or Contributor role
β Azure CLI installed (version 2.50+)
β PowerShell 7+ installed
β kubectl installed (for Kubernetes validation)
β 4+ vCPUs available for VMs
β 50+ GB storage for demo environment
Optional but Recommended:
β Azure Portal familiarity
β Docker Desktop (for container images)
β VS Code with Azure extensions
Difficulty: Intermediate
Cost: $5-20 Azure credits
Lab Architecture
AZURE LOCAL DEPLOYMENT ARCHITECTURE
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
On-Premises (Your Lab Environment)
ββββββββββββββββββββββββββββββββββββββββββββββββ
β Azure Local Cluster (Simulated) β
β β
β βββββββββββββββββββββββββββββββββββββββββββ β
β β Arc Agent β β
β β βββ Connected to Azure (Management) β β
β βββββββββββββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββ β
β β Demo Application Namespace β β
β β ββ Web API Pod β β
β β ββ Database Pod β β
β β ββ Storage PVC β β
β βββββββββββββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββ β
β β Azure Local Storage β β
β β ββ 500 GB for containers β β
β β ββ 500 GB for data β β
β βββββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββ
β (Connected mode)
ββββββββββββββββββββββββββββββββββββββββββββββββ
β Azure (Cloud) β
β ββ Azure Arc representation β
β ββ Monitoring & management β
β ββ Backup storage β
β ββ Audit logs β
ββββββββββββββββββββββββββββββββββββββββββββββββ
Lab Steps
Step 1: Prepare Azure Environment
Objective: Set up Azure resources for Azure Local deployment
Step 1.1: Create Resource Group
# Set variables
$resourceGroup = "rg-azure-local-lab"
$location = "eastus"
# Create resource group
az group create `
--name $resourceGroup `
--location $location
# Verify creation
az group show --name $resourceGroup
Expected Output:
{
"id": "/subscriptions/xxx/resourceGroups/rg-azure-local-lab",
"location": "eastus",
"name": "rg-azure-local-lab",
"properties": {
"provisioningState": "Succeeded"
}
}
Step 1.2: Create Virtual Network
# Create virtual network for Azure Local cluster
az network vnet create `
--resource-group $resourceGroup `
--name "vnet-azure-local" `
--address-prefix "10.0.0.0/16" `
--subnet-name "subnet-nodes" `
--subnet-prefix "10.0.1.0/24"
# Verify creation
az network vnet show `
--resource-group $resourceGroup `
--name "vnet-azure-local"
Expected Output: Virtual network created with CIDR 10.0.0.0/16
Step 1.3: Create Storage Account
# Create storage account for Azure Local backups
$storageAccount = "stazurelocallab$([DateTime]::UtcNow.Ticks % 1000000)"
az storage account create `
--name $storageAccount `
--resource-group $resourceGroup `
--location $location `
--sku "Standard_GRS" `
--kind "StorageV2"
# Create container for backups
az storage container create `
--name "azure-local-backups" `
--account-name $storageAccount
# Verify creation
az storage account show `
--name $storageAccount `
--resource-group $resourceGroup
Expected Output: Storage account created with GRS replication
Step 2: Deploy Azure Local Simulation
Objective: Set up simulated Azure Local environment (AKS cluster representing Azure Local)
Step 2.1: Create AKS Cluster (Simulating Azure Local)
# Note: In production, this would be actual Azure Local hardware
# For this lab, we simulate with AKS cluster
$clusterName = "aks-azure-local-lab"
$nodeCount = 3
# Create AKS cluster
az aks create `
--resource-group $resourceGroup `
--name $clusterName `
--node-count $nodeCount `
--vm-set-type VirtualMachineScaleSets `
--load-balancer-sku standard `
--enable-managed-identity `
--network-plugin azure `
--vnet-subnet-id "/subscriptions/$(az account show --query id -o tsv)/resourceGroups/$resourceGroup/providers/Microsoft.Network/virtualNetworks/vnet-azure-local/subnets/subnet-nodes" `
--docker-bridge-address 172.17.0.1/16 `
--service-cidr 10.1.0.0/16 `
--dns-service-ip 10.1.0.10 `
--enable-addons monitoring `
--workspace-resource-id "/subscriptions/$(az account show --query id -o tsv)/resourceGroups/$resourceGroup/providers/microsoft.operationalinsights/workspaces/..." `
--generate-ssh-keys
# This takes ~5-10 minutes
# Meanwhile, continue with other preparations
Expected Output: AKS cluster creation started (wait for completion)
Step 2.2: Get Cluster Credentials
# Get cluster credentials for kubectl access
az aks get-credentials `
--resource-group $resourceGroup `
--name $clusterName `
--overwrite-existing
# Verify connectivity
kubectl cluster-info
kubectl get nodes
Expected Output:
NAME STATUS ROLES AGE
aks-nodepool1-xxxxx-vmss-000000 Ready agent 2m
aks-nodepool1-xxxxx-vmss-000001 Ready agent 2m
aks-nodepool1-xxxxx-vmss-000002 Ready agent 2m
Step 3: Deploy Sample Application
Objective: Deploy a sample application to validate Azure Local functionality
Step 3.1: Create Application Namespace
# Create namespace for demo application
kubectl create namespace demo-app
# Verify namespace creation
kubectl get namespaces
Expected Output: Namespace βdemo-appβ created
Step 3.2: Create ConfigMap with Application Configuration
# Create ConfigMap for application settings
@"
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: demo-app
data:
app.properties: |
DATABASE_HOST=postgres-db.demo-app.svc.cluster.local
DATABASE_PORT=5432
DATABASE_NAME=demodb
LOG_LEVEL=INFO
"@ | kubectl apply -f -
# Verify
kubectl get configmap -n demo-app
Expected Output: ConfigMap βapp-configβ created
Step 3.3: Deploy PostgreSQL Database
# Create persistent volume claim for database
@"
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
namespace: demo-app
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
"@ | kubectl apply -f -
# Deploy PostgreSQL
@"
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres-db
namespace: demo-app
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:15-alpine
ports:
- containerPort: 5432
env:
- name: POSTGRES_DB
value: demodb
- name: POSTGRES_USER
value: demouser
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: password
volumeMounts:
- name: postgres-storage
mountPath: /var/lib/postgresql/data
volumes:
- name: postgres-storage
persistentVolumeClaim:
claimName: postgres-pvc
---
apiVersion: v1
kind: Service
metadata:
name: postgres-db
namespace: demo-app
spec:
ports:
- port: 5432
targetPort: 5432
selector:
app: postgres
"@ | kubectl apply -f -
# Monitor deployment
kubectl get deployment -n demo-app
kubectl get pods -n demo-app
Expected Output: PostgreSQL pod running
Step 3.4: Deploy Web Application
# Deploy sample web API
@"
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-api
namespace: demo-app
spec:
replicas: 3
selector:
matchLabels:
app: web-api
template:
metadata:
labels:
app: web-api
spec:
containers:
- name: web-api
image: nginx:latest
ports:
- containerPort: 80
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
envFrom:
- configMapRef:
name: app-config
---
apiVersion: v1
kind: Service
metadata:
name: web-api
namespace: demo-app
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 80
selector:
app: web-api
"@ | kubectl apply -f -
# Monitor deployment
kubectl get deployment -n demo-app
kubectl get service -n demo-app
Expected Output: Web API pods running with load balancer service
Step 4: Validate Deployment
Objective: Verify all components deployed successfully
Step 4.1: Check Pod Status
# Get all pods in demo-app namespace
kubectl get pods -n demo-app -o wide
# Check for any issues
kubectl get events -n demo-app
Expected Output:
NAME READY STATUS RESTARTS AGE
postgres-db-xxxxx-xxxxx 1/1 Running 0 2m
web-api-xxxxx-xxxxx-xxxxx 1/1 Running 0 1m
web-api-xxxxx-xxxxx-yyyyy 1/1 Running 0 1m
web-api-xxxxx-xxxxx-zzzzz 1/1 Running 0 1m
Step 4.2: Check Storage Usage
# Get persistent volume status
kubectl get pv
kubectl get pvc -n demo-app
# Check storage class
kubectl get storageclass
Expected Output: PVC showing βBoundβ status with correct storage size
Step 4.3: Test Application Connectivity
# Get external IP of web service
kubectl get service web-api -n demo-app
# Test connectivity (replace with actual IP)
$webApiIP = kubectl get service web-api -n demo-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
Invoke-WebRequest "http://$webApiIP" -ErrorAction SilentlyContinue
Expected Output: HTTP 200 OK response
Step 4.4: Check Resource Utilization
# Get node status
kubectl get nodes -o wide
# Get cluster metrics (if metrics-server installed)
kubectl top nodes
kubectl top pods -n demo-app
Expected Output: Nodes showing Ready status with CPU/Memory usage
Step 5: Connected Mode Configuration
Objective: Configure Azure Local connectivity to Azure (via Arc)
Step 5.1: Install Azure Arc Agent
# Add Helm repository for Azure Arc
helm repo add azurearc https://raw.githubusercontent.com/Azure/azure-arc-for-kubernetes/main/helm
# Install Azure Arc extension
az k8s-configuration flux create `
--cluster-name $clusterName `
--resource-group $resourceGroup `
--cluster-type managedClusters `
--name arc-demo-config `
--scope cluster
# Verify Arc connectivity
kubectl get pods -n azure-arc
Expected Output: Azure Arc pods running in azure-arc namespace
Step 5.2: Enable Monitoring
# Enable Container Insights (monitoring)
# Get cluster resource ID
$clusterId = az aks show `
--resource-group $resourceGroup `
--name $clusterName `
--query id `
-o tsv
# Enable monitoring
az aks enable-addons `
--addons monitoring `
--name $clusterName `
--resource-group $resourceGroup
# Verify monitoring is enabled
kubectl get daemonset -n kube-system | grep -i omsagent
Expected Output: OMS Agent daemonset running for monitoring
Step 5.3: Verify Azure Connectivity
# Check Arc connection status via Azure CLI
az connectedk8s show `
--name $clusterName `
--resource-group $resourceGroup
# Verify agent connectivity
kubectl get deploy -n azure-arc
Expected Output: Connected status showing βConnectedβ
Step 6: Backup Configuration
Objective: Configure backup for disaster recovery
Step 6.1: Create Backup Policy
# In production, use Azure Backup
# For this lab, we configure snapshot strategy
# Create snapshot schedule
@"
apiVersion: v1
kind: ConfigMap
metadata:
name: backup-schedule
namespace: demo-app
data:
schedule.json: |
{
"frequency": "daily",
"time": "02:00Z",
"retention_days": 30,
"backup_location": "$storageAccount"
}
"@ | kubectl apply -f -
# Verify backup configuration
kubectl get configmap -n demo-app
Expected Output: Backup schedule configured
Step 6.2: Test Backup
# Create test backup
$backupName = "backup-$(Get-Date -Format yyyyMMdd-HHmm)"
# Export current state to Azure Storage
kubectl get all --all-namespaces -o yaml | `
Out-String | `
az storage blob upload `
--account-name $storageAccount `
--container-name azure-local-backups `
--name "$backupName.yaml" `
--file -
# Verify backup created
az storage blob list `
--account-name $storageAccount `
--container-name azure-local-backups
Expected Output: Backup file uploaded successfully
Step 7: Post-Lab Validation
Objective: Confirm all lab objectives completed
Step 7.1: Collect Evidence
# Document cluster state
kubectl cluster-info > cluster-info.txt
kubectl get all --all-namespaces > all-resources.txt
kubectl get nodes -o wide > nodes-status.txt
kubectl top nodes > resource-usage.txt
# Document Azure resources
az group show --name $resourceGroup > resource-group-info.json
az aks show --name $clusterName --resource-group $resourceGroup > cluster-details.json
Write-Host "Lab evidence collected in current directory"
Step 7.2: Summary Report
# Generate summary
Write-Host "=== LAB 1 COMPLETION SUMMARY ===" -ForegroundColor Green
Write-Host "`nResource Group: $resourceGroup"
Write-Host "Cluster Name: $clusterName"
Write-Host "Node Count: $nodeCount"
Write-Host "Storage Account: $storageAccount"
Write-Host "`nDeployments:"
kubectl get deployment --all-namespaces
Write-Host "`nServices:"
kubectl get service --all-namespaces
Write-Host "`nStorage:"
kubectl get pvc --all-namespaces
Write-Host "`n=== LAB 1 COMPLETE ===" -ForegroundColor Green
Cleanup (Optional)
# Delete all resources to avoid unnecessary charges
az group delete `
--name $resourceGroup `
--yes `
--no-wait
Write-Host "Resource group deletion initiated"
Learning Outcomes
What You Learned
β Azure Local architecture and deployment model β Connected mode configuration for hybrid connectivity β Kubernetes deployment on Azure Local/AKS β Persistent storage configuration β Application deployment and validation β Monitoring integration with Azure β Backup strategy for disaster recovery
Skills Gained
β Use Azure CLI for infrastructure provisioning β Deploy and manage Kubernetes resources β Configure networking for hybrid deployments β Implement persistent storage for stateful applications β Monitor cluster health and performance β Backup and recovery procedures
Next Steps
- Lab 2: Azure Arc Onboarding - Register Azure Local resources with Arc
- Lab 3: Edge RAG Setup - Deploy AI workload on Azure Local
- Production: Deploy Azure Local on actual hardware in production datacenter
Troubleshooting
| Issue | Solution |
|---|---|
| AKS cluster creation timeout | Increase timeout or check Azure quota |
| Pods in CrashLoopBackOff | Check logs: kubectl logs <pod> -n <namespace> |
| PVC not binding | Verify storage class: kubectl get storageclass |
| LoadBalancer IP pending | Add ingress controller or use NodePort |
| Low disk space | Clean up images: kubectl clean or scale down |
Last Updated: October 21, 2025