Module 10 Public Cloud 5 labs

Microsoft Azure Core for DevOps

Nền tảng Azure cho DevOps Engineer: Entra ID, RBAC, VNet, VM, App Service, ACR/AKS, Key Vault và Azure Monitor — thực hành bằng Azure CLI và Azure PowerShell thật.

Công cụ thực hành Azure CLI 2.x, Azure PowerShell, Docker, kubectl, Azure Portal
Nền tảng Public Cloud — Microsoft Azure
Thời điểm phát hành 23/05/2026
Ngày biên soạn 23/05/2026
Người biên soạn Trần Văn Hòa — Microsoft Certified Trainer (MCT)

Mục tiêu học tập

1. Lý thuyết cốt lõi

1.1. Kiến trúc tổ chức Azure — Tenant, Subscription, Resource Group

Azure tổ chức tài nguyên theo 4 cấp:

1.2. Entra ID (Azure Active Directory) và RBAC

Các khái niệm RBAC cốt lõi

  • Security Principal: User, Group, Service Principal, Managed Identity — ai thực hiện hành động.
  • Role Definition: tập hợp permission (Owner / Contributor / Reader + custom); định nghĩa được làm gì.
  • Scope: Management Group / Subscription / Resource Group / Resource — áp dụng ở đâu.
  • Role Assignment: gán Role Definition cho Security Principal tại Scope. Role assignment kế thừa downward.
  • Managed Identity: Service Principal tự động quản lý credential — không cần lưu secret; System-assigned (gắn với 1 resource) hoặc User-assigned (dùng chung).

1.3. Networking — VNet, Subnet, NSG, VNet Peering

Virtual Network (VNet) là mạng riêng ảo trong Azure, tương đương VPC của AWS. Các thành phần chính:

Thành phầnChức năngSo với AWS
VNetMạng ảo riêng, CIDR tùy chọnVPC
SubnetPhân đoạn VNet, liên kết NSG/Route TableSubnet
NSGStateful firewall rule, gán vào NIC hoặc subnetSecurity Group
Azure FirewallFQDN filtering, threat intel, egress controlNetwork Firewall
VNet PeeringKết nối 2 VNet (cùng/khác region) low-latencyVPC Peering
Private EndpointExpose PaaS service (Storage/KeyVault/ACR) vào VNet qua private IPVPC Endpoint

1.4. Compute — Azure VM, App Service, AKS

1.5. Key Vault — Secrets, Keys, Certificates

Key Vault tập trung quản lý bí mật ứng dụng, khóa mã hóa và chứng chỉ SSL. Không bao giờ hardcode secret vào code hay environment variable plaintext. Pattern chuẩn cho DevOps: App (VM / App Service / AKS pod) dùng Managed Identity để xác thực với Key Vault — không cần lưu credential ở bất kỳ đâu. Soft-delete và Purge Protection ngăn xóa nhầm. Tiered access: Key Vault access policy (classic) hoặc Azure RBAC for Key Vault (khuyến nghị).

1.6. Observability — Azure Monitor, Log Analytics, Application Insights

Azure Monitor là nền tảng quan sát thống nhất: thu Metrics (số liệu thời gian thực) và Logs (structured log). Log Analytics Workspace: store và query log bằng KQL (Kusto Query Language). Application Insights: APM cho app — distributed tracing, dependency map, performance, exceptions. Alerts: trigger dựa trên metric threshold, log query hoặc activity log — action group gửi email/SMS/webhook/ITSM.

2. Thực hành (Labs)

LAB-046

Tạo Resource Group, VNet và RBAC bằng Azure CLI + PowerShell

Azure CLI · Azure PowerShell · Azure Portal

🎯 Mục tiêu: Tạo Resource Group và VNet 3-subnet bằng Azure CLI; tạo Service Principal và gán role Contributor bằng Azure PowerShell; kiểm chứng bằng Portal.

🧰 Công cụ / nền tảng: Azure CLI 2.x (az version), Azure PowerShell module Az (Get-Module Az -ListAvailable).

📦 Chuẩn bị: Cài Azure CLI (winget install Microsoft.AzureCLI) và Az PowerShell module (Install-Module Az -Scope CurrentUser). Đăng nhập: az login / Connect-AzAccount.

▶️ Phần 1: Azure CLI — Resource Group và VNet

# Biến môi trường
RG="rg-devops-lab"
LOCATION="southeastasia"
VNET="vnet-devops"

# 1. Tạo Resource Group
az group create \
  --name $RG \
  --location $LOCATION \
  --tags Project=DevOpsLab Owner=HoaTran

# 2. Tạo VNet
az network vnet create \
  --resource-group $RG \
  --name $VNET \
  --address-prefix 10.0.0.0/16 \
  --location $LOCATION

# 3. Tạo 3 subnet
az network vnet subnet create \
  --resource-group $RG --vnet-name $VNET \
  --name snet-public \
  --address-prefix 10.0.1.0/24

az network vnet subnet create \
  --resource-group $RG --vnet-name $VNET \
  --name snet-app \
  --address-prefix 10.0.11.0/24

az network vnet subnet create \
  --resource-group $RG --vnet-name $VNET \
  --name snet-data \
  --address-prefix 10.0.21.0/24

# 4. Tạo NSG và gán cho snet-app
az network nsg create \
  --resource-group $RG \
  --name nsg-app

# Cho phép HTTP từ snet-public
az network nsg rule create \
  --resource-group $RG --nsg-name nsg-app \
  --name AllowHTTPFromPublic \
  --priority 100 \
  --source-address-prefixes 10.0.1.0/24 \
  --destination-port-ranges 80 443 \
  --protocol Tcp --access Allow --direction Inbound

az network vnet subnet update \
  --resource-group $RG --vnet-name $VNET \
  --name snet-app \
  --network-security-group nsg-app

# 5. Xem kết quả
az network vnet show \
  --resource-group $RG --name $VNET \
  --query '{name:name,addressSpace:addressSpace.addressPrefixes,subnets:subnets[*].{name:name,prefix:addressPrefix}}'

▶️ Phần 2: Azure PowerShell — Service Principal và RBAC

# Chạy trong PowerShell (sau Connect-AzAccount)

# 1. Lấy Subscription ID
$SubId = (Get-AzContext).Subscription.Id
Write-Host "Subscription: $SubId"

# 2. Tạo Service Principal với role Contributor trên Resource Group
$sp = New-AzADServicePrincipal `
  -DisplayName "sp-devops-cicd" `
  -Role "Contributor" `
  -Scope "/subscriptions/$SubId/resourceGroups/rg-devops-lab"

# 3. Hiển thị credentials (lưu lại — chỉ hiển thị 1 lần)
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sp.PasswordCredentials.SecretText)
$plainPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
Write-Host "AppId (ClientId): $($sp.AppId)"
Write-Host "TenantId: $((Get-AzContext).Tenant.Id)"
Write-Host "Password: $plainPassword"  # Lưu vào Key Vault ngay!

# 4. Kiểm tra role assignment
Get-AzRoleAssignment `
  -ServicePrincipalName $sp.AppId `
  -ResourceGroupName "rg-devops-lab" |
  Select-Object DisplayName, RoleDefinitionName, Scope

# 5. Test đăng nhập bằng Service Principal
az login --service-principal `
  --username $sp.AppId `
  --password $plainPassword `
  --tenant (Get-AzContext).Tenant.Id
az group list --query "[?name=='rg-devops-lab']"

🖥️ Đối chiếu Azure Portal:

Resource Groups → rg-devops-lab → Virtual Networks → vnet-devops: thấy 3 subnet. Tab Access control (IAM) của RG: Role assignments → sp-devops-cicd có Contributor.

✅ Kết quả mong đợi: az network vnet show hiển thị 3 subnet đúng CIDR. Get-AzRoleAssignment liệt kê sp-devops-cicd với role Contributor. Service principal đăng nhập CLI thành công, list được Resource Group.

🧹 Cleanup:

# PowerShell
Remove-AzADServicePrincipal -DisplayName "sp-devops-cicd" -Force

# Azure CLI (xóa RG xóa toàn bộ tài nguyên bên trong)
az group delete --name rg-devops-lab --yes --no-wait
LAB-047

Deploy ứng dụng lên App Service bằng Azure CLI

Azure CLI · Azure Portal

🎯 Mục tiêu: Tạo App Service Plan (Linux, Python), deploy Flask app bằng zip deploy; thêm deployment slot staging; test slot swap (blue/green).

🧰 Công cụ / nền tảng: Azure CLI, Python 3.12, zip.

📦 Chuẩn bị: RG rg-devops-lab đã có (LAB-046). Cần Python + pip cài cục bộ.

▶️ Các bước:

RG="rg-devops-lab"
PLAN="asp-devops"
APP_NAME="devops-flask-$(date +%s)"  # unique name

# 1. Tạo App Service Plan (Linux B1 — billable; dùng F1 free cho lab nhỏ)
az appservice plan create \
  --resource-group $RG \
  --name $PLAN \
  --is-linux \
  --sku B1 \
  --location southeastasia

# 2. Tạo Web App
az webapp create \
  --resource-group $RG \
  --plan $PLAN \
  --name $APP_NAME \
  --runtime "PYTHON:3.12"

# 3. Tạo Flask app cục bộ
mkdir -p /tmp/flask-lab && cd /tmp/flask-lab
cat > app.py << 'EOF'
from flask import Flask
import os
app = Flask(__name__)

@app.route("/")
def home():
    env = os.environ.get("APP_ENV", "production")
    return f"

Hello from App Service!

Environment: {env}

" @app.route("/healthz") def health(): return "OK", 200 if __name__ == "__main__": app.run(host="0.0.0.0", port=8000) EOF cat > requirements.txt << 'EOF' flask==3.1.0 gunicorn==22.0.0 EOF # startup command echo "gunicorn --bind=0.0.0.0:8000 app:app" > startup.txt # 4. Zip và deploy cd /tmp/flask-lab zip -r app.zip . -x "*.pyc" "__pycache__/*" az webapp deploy \ --resource-group $RG \ --name $APP_NAME \ --src-path app.zip \ --type zip # 5. Set startup command và env var az webapp config set \ --resource-group $RG --name $APP_NAME \ --startup-file "gunicorn --bind=0.0.0.0:8000 app:app" az webapp config appsettings set \ --resource-group $RG --name $APP_NAME \ --settings APP_ENV=production SCM_DO_BUILD_DURING_DEPLOYMENT=true # 6. Tạo staging slot az webapp deployment slot create \ --resource-group $RG --name $APP_NAME \ --slot staging \ --configuration-source $APP_NAME # Deploy phiên bản mới lên staging sed -i 's/production/staging/g' /tmp/flask-lab/app.py cd /tmp/flask-lab && zip -r app-staging.zip . -x "*.pyc" az webapp deploy \ --resource-group $RG --name $APP_NAME \ --slot staging --src-path app-staging.zip --type zip # Test staging STAGING_URL="https://${APP_NAME}-staging.azurewebsites.net" echo "Staging: $STAGING_URL" curl -s $STAGING_URL # 7. Slot swap (production ← staging) az webapp deployment slot swap \ --resource-group $RG --name $APP_NAME \ --slot staging --target-slot production PROD_URL="https://${APP_NAME}.azurewebsites.net" echo "Production after swap: $PROD_URL" curl -s $PROD_URL

🖥️ Đối chiếu Azure Portal:

App Services → devops-flask-xxx → Overview: URL, Status Running, Runtime stack Python 3.12. Deployment slots: thấy staging. Configuration → Application settings: APP_ENV.

✅ Kết quả mong đợi: curl https://devops-flask-xxx.azurewebsites.net trả về trang HTML. Sau swap: production hiển thị "staging" — xác nhận blue/green swap thành công. curl .../healthz trả về OK.

🧹 Cleanup:

az webapp delete --resource-group $RG --name $APP_NAME
az appservice plan delete --resource-group $RG --name $PLAN --yes
LAB-048

Push Docker image lên Azure Container Registry (ACR)

Azure CLI · Docker · Azure Portal

🎯 Mục tiêu: Tạo ACR private registry, build và push image Nginx tùy chỉnh; bật image scanning; tạo token để CI/CD pipeline pull image không cần admin credential.

🧰 Công cụ / nền tảng: Azure CLI, Docker Desktop.

📦 Chuẩn bị: Docker daemon đang chạy (docker info). RG rg-devops-lab đã có.

▶️ Các bước:

RG="rg-devops-lab"
# ACR name: chỉ lowercase alphanumeric, globally unique
ACR_NAME="acrdevopslab$(date +%s | tail -c 6)"

# 1. Tạo ACR (Standard tier: geo-replication + content trust available)
az acr create \
  --resource-group $RG \
  --name $ACR_NAME \
  --sku Standard \
  --location southeastasia \
  --admin-enabled false

ACR_LOGIN_SERVER=$(az acr show \
  --name $ACR_NAME --query loginServer --output tsv)
echo "ACR: $ACR_LOGIN_SERVER"

# 2. Build image Nginx tùy chỉnh
mkdir -p /tmp/acr-lab
cat > /tmp/acr-lab/Dockerfile << 'EOF'
FROM nginx:1.27-alpine
RUN echo "

DevOps App on AKS via ACR

" > /usr/share/nginx/html/index.html EXPOSE 80 EOF docker build -t devops-app:1.0 /tmp/acr-lab # 3. Login ACR bằng Azure CLI (dùng token ngắn hạn, không cần admin password) az acr login --name $ACR_NAME # 4. Tag và push docker tag devops-app:1.0 ${ACR_LOGIN_SERVER}/devops-app:1.0 docker push ${ACR_LOGIN_SERVER}/devops-app:1.0 # 5. Bật vulnerability scanning (Microsoft Defender for Containers) az acr update --name $ACR_NAME \ --resource-group $RG # Xem repositories az acr repository list --name $ACR_NAME --output table # Xem tags az acr repository show-tags \ --name $ACR_NAME \ --repository devops-app \ --output table # 6. Tạo scope map + token cho CI/CD (pull-only, không cần admin) az acr scope-map create \ --name cicd-pull-map \ --registry $ACR_NAME \ --repository devops-app content/read \ --description "Pull-only access for CI/CD" az acr token create \ --name cicd-pull-token \ --registry $ACR_NAME \ --scope-map cicd-pull-map \ --query "credentials.passwords[0].value" \ --output tsv # Lưu output này vào Key Vault (LAB-050) thay vì hardcode # 7. Kiểm tra image size và layer az acr manifest list-metadata \ --registry $ACR_NAME \ --name devops-app \ --query "[*].{digest:digest,size:imageSize,created:createdTime}" \ --output table

🖥️ Đối chiếu Azure Portal:

Container registries → acrdevopslab... → Repositories: thấy devops-app, click vào thấy tag 1.0. Tokens: thấy cicd-pull-token Active. Admin account = Disabled.

✅ Kết quả mong đợi: az acr repository list trả về ["devops-app"]. show-tags trả về ["1.0"]. Token tồn tại với status Active. Pull test: docker pull ${ACR_LOGIN_SERVER}/devops-app:1.0 thành công.

🧹 Cleanup:

az acr token delete --name cicd-pull-token --registry $ACR_NAME --yes
az acr delete --name $ACR_NAME --resource-group $RG --yes
LAB-049

Tạo AKS cluster và deploy workload từ ACR

Azure CLI · kubectl · Helm · Azure Portal

🎯 Mục tiêu: Tạo AKS cluster 2 node; attach ACR để pull image không cần imagePullSecret thủ công; deploy Deployment + Service; scale và rolling update.

🧰 Công cụ / nền tảng: Azure CLI, kubectl, helm (tùy chọn).

📦 Chuẩn bị: ACR từ LAB-048 còn tồn tại (có image devops-app:1.0). Cài kubectl: az aks install-cli.

▶️ Các bước:

RG="rg-devops-lab"
CLUSTER="aks-devops"
ACR_NAME="acrdevopslab"  # thay bằng tên ACR đã tạo
ACR_LOGIN_SERVER=$(az acr show --name $ACR_NAME --query loginServer --output tsv)

# 1. Tạo AKS cluster (System node pool 2 node B2s)
az aks create \
  --resource-group $RG \
  --name $CLUSTER \
  --node-count 2 \
  --node-vm-size Standard_B2s \
  --enable-managed-identity \
  --attach-acr $ACR_NAME \
  --network-plugin azure \
  --generate-ssh-keys \
  --location southeastasia
# Mất ~5 phút

# 2. Lấy kubeconfig
az aks get-credentials \
  --resource-group $RG \
  --name $CLUSTER \
  --overwrite-existing

kubectl get nodes -o wide

# 3. Deploy workload từ ACR
IMAGE="${ACR_LOGIN_SERVER}/devops-app:1.0"

kubectl create deployment devops-app \
  --image=$IMAGE \
  --replicas=2 \
  --port=80

# 4. Expose qua LoadBalancer
kubectl expose deployment devops-app \
  --type=LoadBalancer \
  --port=80 \
  --target-port=80 \
  --name=devops-app-svc

# Chờ External IP
kubectl get svc devops-app-svc -w

# 5. Xem pods và logs
kubectl get pods -o wide
kubectl logs -l app=devops-app --tail=20

# 6. Scale lên 4 replicas
kubectl scale deployment devops-app --replicas=4
kubectl rollout status deployment/devops-app

# 7. Rolling update: push image v2 rồi update
sed -i 's/>DevOps App/>DevOps App v2/g' /tmp/acr-lab/Dockerfile
az acr login --name $ACR_NAME
docker build -t ${ACR_LOGIN_SERVER}/devops-app:2.0 /tmp/acr-lab
docker push ${ACR_LOGIN_SERVER}/devops-app:2.0

kubectl set image deployment/devops-app \
  devops-app=${ACR_LOGIN_SERVER}/devops-app:2.0
kubectl rollout status deployment/devops-app
kubectl rollout history deployment/devops-app

# 8. Rollback nếu cần
# kubectl rollout undo deployment/devops-app

# 9. Xem Container Insights (nếu enabled)
az aks enable-addons \
  --resource-group $RG --name $CLUSTER \
  --addons monitoring

🖥️ Đối chiếu Azure Portal:

Kubernetes services → aks-devops → Workloads: Deployment devops-app 4/4 Running. Services: devops-app-svc External IP tồn tại. Insights → Nodes/Pods tab hiển thị CPU/Memory usage.

✅ Kết quả mong đợi: kubectl get pods hiển thị 4 pods Running. curl http://<EXTERNAL-IP> trả về HTML. Sau rolling update: kubectl rollout history có revision 1 và 2; curl trả về "v2". kubectl rollout undo hoàn nguyên về v1 trong vòng <30 giây.

🧹 Cleanup:

kubectl delete svc devops-app-svc
kubectl delete deployment devops-app
az aks delete --resource-group $RG --name $CLUSTER --yes --no-wait
LAB-050

Key Vault secret injection cho App Service qua Managed Identity và Azure Monitor Alert

Azure CLI · Azure PowerShell · Azure Portal

🎯 Mục tiêu: Tạo Key Vault, lưu database connection string; cấu hình App Service dùng System-assigned Managed Identity để đọc secret — không hardcode credential; tạo Azure Monitor Alert khi HTTP 5xx tăng cao.

🧰 Công cụ / nền tảng: Azure CLI, Azure PowerShell.

📦 Chuẩn bị: App Service từ LAB-047 đang tồn tại ($APP_NAME). RG rg-devops-lab.

▶️ Phần 1: Key Vault + Managed Identity (Azure CLI)

RG="rg-devops-lab"
KV_NAME="kv-devops-$(date +%s | tail -c 6)"
# APP_NAME hãy thay bằng tên App Service từ LAB-047

# 1. Tạo Key Vault với RBAC authorization (khuyến nghị hơn Access Policy)
az keyvault create \
  --resource-group $RG \
  --name $KV_NAME \
  --location southeastasia \
  --enable-rbac-authorization true \
  --retention-days 7 \
  --sku standard

# 2. Lưu secret vào Key Vault
az keyvault secret set \
  --vault-name $KV_NAME \
  --name "db-connection-string" \
  --value "Server=mydb.postgres.database.azure.com;Database=appdb;User=appuser;Password=S3cret!"

az keyvault secret set \
  --vault-name $KV_NAME \
  --name "api-key" \
  --value "sk-prod-abc123xyz789"

# Xác nhận secret tồn tại (chỉ hiện metadata, không hiện value)
az keyvault secret list --vault-name $KV_NAME --output table

# 3. Bật System-assigned Managed Identity cho App Service
az webapp identity assign \
  --resource-group $RG \
  --name $APP_NAME

IDENTITY_PRINCIPAL=$(az webapp identity show \
  --resource-group $RG --name $APP_NAME \
  --query principalId --output tsv)
echo "Managed Identity Principal: $IDENTITY_PRINCIPAL"

KV_RESOURCE_ID=$(az keyvault show \
  --name $KV_NAME --query id --output tsv)

# 4. Gán role "Key Vault Secrets User" cho Managed Identity
az role assignment create \
  --assignee $IDENTITY_PRINCIPAL \
  --role "Key Vault Secrets User" \
  --scope $KV_RESOURCE_ID

# 5. Cấu hình App Service reference secret từ KV (Key Vault Reference)
KV_SECRET_URI=$(az keyvault secret show \
  --vault-name $KV_NAME --name db-connection-string \
  --query id --output tsv)
# Xóa version ở cuối URI để luôn lấy version mới nhất
KV_SECRET_URI_NO_VER=$(echo $KV_SECRET_URI | sed 's|/[^/]*$||')

az webapp config appsettings set \
  --resource-group $RG --name $APP_NAME \
  --settings "[email protected](SecretUri=${KV_SECRET_URI_NO_VER}/)"

# 6. Xem app setting (value hiện là @Microsoft.KeyVault reference, không lộ secret)
az webapp config appsettings list \
  --resource-group $RG --name $APP_NAME \
  --query "[?name=='DB_CONNECTION_STRING']"

▶️ Phần 2: Azure Monitor Alert và Log Analytics (Azure PowerShell)

# PowerShell — sau Connect-AzAccount

$RG = "rg-devops-lab"
$Location = "southeastasia"

# 1. Tạo Log Analytics Workspace
$Law = New-AzOperationalInsightsWorkspace `
  -ResourceGroupName $RG `
  -Name "law-devops-lab" `
  -Location $Location `
  -Sku PerGB2018 `
  -RetentionInDays 30
Write-Host "Workspace ID: $($Law.CustomerId)"

# 2. Tạo Action Group (email notification)
$EmailReceiver = New-AzActionGroupReceiver `
  -Name "admin-email" `
  -EmailReceiver `
  -EmailAddress "[email protected]"

$ActionGroup = Set-AzActionGroup `
  -ResourceGroupName $RG `
  -Name "ag-devops-alerts" `
  -ShortName "DevOpsAG" `
  -Receiver $EmailReceiver

$ActionGroupId = $ActionGroup.Id

# 3. Lấy App Service Resource ID
$AppId = (Get-AzWebApp -ResourceGroupName $RG -Name $env:APP_NAME).Id
# Nếu biến shell không tự động chuyển, thay $env:APP_NAME bằng tên app thật

# 4. Tạo Metric Alert: HTTP 5xx > 5 trong 5 phút
Add-AzMetricAlertRuleV2 `
  -Name "alert-http5xx" `
  -ResourceGroupName $RG `
  -WindowSize ([TimeSpan]::FromMinutes(5)) `
  -Frequency ([TimeSpan]::FromMinutes(1)) `
  -TargetResourceId $AppId `
  -Condition (New-AzMetricAlertRuleV2Criteria `
    -MetricName "Http5xx" `
    -MetricNamespace "Microsoft.Web/sites" `
    -TimeAggregation Total `
    -Operator GreaterThan `
    -Threshold 5) `
  -ActionGroupId $ActionGroupId `
  -Severity 2 `
  -Description "HTTP 5xx errors exceed 5 in 5 minutes"

# 5. Liệt kê alert rules
Get-AzMetricAlertRuleV2 -ResourceGroupName $RG |
  Select-Object Name, Severity, Enabled |
  Format-Table

# 6. Simulate 5xx: trả về error từ app endpoint /error
# curl https://.azurewebsites.net/error  (nếu route chưa có sẽ trả 404)
# Có thể thêm route /error vào Flask app trả 500 để test

# 7. Query Log Analytics với KQL
$Query = @"
AzureActivity
| where ResourceGroup == 'rg-devops-lab'
| where TimeGenerated > ago(1h)
| summarize count() by OperationNameValue, ActivityStatusValue
| order by count_ desc
| take 20
"@
Invoke-AzOperationalInsightsQuery `
  -WorkspaceId $Law.CustomerId `
  -Query $Query |
  Select-Object -ExpandProperty Results

🖥️ Đối chiếu Azure Portal:

Key vaults → kv-devops-xxx → Secrets: thấy db-connection-stringapi-key (value ẩn). App Service → Configuration → Application settings: DB_CONNECTION_STRING hiển thị Key vault Reference với icon xanh (resolved). Monitor → Alerts → Alert rules: thấy alert-http5xx.

✅ Kết quả mong đợi: Key Vault Reference trong App Service hiện status Resolved (không phải Invalid) — nghĩa là Managed Identity có quyền đọc secret thành công. Alert rule tồn tại Severity 2, Enabled. Invoke-AzOperationalInsightsQuery trả về activity log của RG.

🧹 Cleanup:

# Azure CLI
az keyvault delete --name $KV_NAME --resource-group $RG
az keyvault purge --name $KV_NAME --location southeastasia

# PowerShell
Remove-AzMetricAlertRuleV2 -Name "alert-http5xx" -ResourceGroupName $RG
Remove-AzActionGroup -Name "ag-devops-alerts" -ResourceGroupName $RG
Remove-AzOperationalInsightsWorkspace `
  -ResourceGroupName $RG -Name "law-devops-lab" -ForceDelete -Force

# Xóa toàn bộ RG nếu đã xong tất cả lab
az group delete --name rg-devops-lab --yes --no-wait

3. Tình huống doanh nghiệp thực tế

Bối cảnh

Một công ty logistics Việt Nam có hệ thống ERP on-prem (Windows Server + SQL Server) muốn hiện đại hóa: tách microservice tracking lên Azure, giữ core ERP on-prem, đảm bảo security và compliance ISO 27001. Đội IT hiện tại quen Microsoft stack.

Kiến trúc đề xuất

  • Entra ID + RBAC: SSO cho nhân viên qua Entra ID; Managed Identity cho mọi service trên Azure — không có password nào lưu trong code hay pipeline. Conditional Access yêu cầu MFA cho admin.
  • Networking: Hub-Spoke VNet; Hub chứa Azure Firewall + VPN Gateway kết nối on-prem; Spoke-App chứa AKS; Private Endpoint cho ACR và Key Vault — traffic không ra internet.
  • App Platform: AKS với Workload Identity (IRSA tương đương Azure) cho pod; App Service cho API gateway (managed TLS, easy slot swap); Azure Front Door cho global routing + WAF.
  • Secrets Management: Key Vault với Soft Delete + Purge Protection; tất cả secret rotate định kỳ qua Key Vault rotation policy; không một developer nào thấy production secret.
  • Observability: Log Analytics Workspace trung tâm thu log từ AKS (Container Insights), App Service, Azure SQL, Azure Firewall; Dashboard Azure Workbook cho NOC; Alert → Teams webhook cho on-call.
  • DevOps Pipeline: Azure DevOps Pipelines; image build → ACR → AKS rolling deploy; Terraform state lưu Azure Blob với RBAC; Policy-as-Code dùng Azure Policy ngăn deploy tài nguyên không có tag.

📚 Nguồn tham khảo

Module 09: AWS Core for DevOps Module 11: Google Cloud Platform Core
Zalo