AZ-500 LAB 17 ~60 phút Chương 04 Managed Identity

System-assigned Managed Identity cho VM

Bật system-assigned managed identity cho Azure VM, gán quyền Key Vault Secrets User (RBAC), đăng nhập VM và dùng Azure Instance Metadata Service (IMDS) lấy access token — truy cập Azure resource mà không lưu bất kỳ credential tĩnh nào.

🎯 Mục Tiêu Lab

Tạo VM Ubuntu và bật system-assigned managed identity

Tạo Key Vault, thêm secret, cấu hình RBAC permission model

Gán role Key Vault Secrets User cho managed identity của VM

Dùng IMDS endpoint 169.254.169.254 để lấy access token từ bên trong VM

Đọc secret từ Key Vault dùng token lấy qua IMDS — zero stored credential

Phân biệt system-assigned MI và user-assigned MI về lifecycle và tái sử dụng

📋 Chuẩn Bị

Yêu cầu:
  • Azure subscription với quyền Owner hoặc Contributor + User Access Administrator
  • Azure CLI 2.50+ đăng nhập
  • SSH client (OpenSSH trên Windows 10+, Terminal trên macOS/Linux)
  • Region: southeastasia (Singapore)
Lưu ý chi phí:
  • VM Standard_B1s ~$0.012/giờ — xóa ngay sau lab
  • Key Vault Standard tier ~$0.03/10,000 operations
  • Managed identity hoàn toàn miễn phí
  • Tổng lab ~$0.05 nếu hoàn thành trong 1 giờ

🏗️ Kịch Bản

Một VM đang chạy script backup cần đọc connection string được lưu trong Key Vault. Cách cũ: hardcode credential trong script hoặc env variable — cực kỳ nguy hiểm. Giải pháp đúng: bật system-assigned managed identity cho VM, gán quyền đọc secret từ Key Vault, và script dùng IMDS endpoint http://169.254.169.254 để lấy token tự động — không lưu bất kỳ secret nào trên VM.

🧪 Các Bước Thực Hiện

1

Tạo Resource Group, Key Vault và VM

Tạo hạ tầng cần thiết: resource group, Key Vault với RBAC model, một secret, và VM Ubuntu nhỏ.

Cách 1 — Portal
  1. 1.1Portal → Resource groups+ Create → Name: rg-az500-mi-lab17, Region: Southeast Asia
  2. 1.2Tạo Key Vault: name kv-az500-lab17-[suffix], Region: Southeast Asia, Permission model: Azure role-based access control
  3. 1.3Trong Key Vault → Secrets+ Generate/Import → Name: db-connection-string, Value: Server=sql-lab17;Database=appdb;...
  4. 1.4Tạo VM: Virtual machines → Image: Ubuntu 22.04 LTS, Size: B1s, Authentication: SSH public key, Public inbound ports: SSH (22)
Cách 2 — Azure CLI
Azure CLI— tạo RG, Key Vault, secret và VM
# Biến môi trường
RG="rg-az500-mi-lab17"
LOCATION="southeastasia"
KV_NAME="kv-az500-lab17-$(date +%s | tail -c 5)"
VM_NAME="vm-az500-lab17"
SUBSCRIPTION_ID=$(az account show --query id -o tsv)

# Tạo Resource Group
az group create --name $RG --location $LOCATION

# Tạo Key Vault với RBAC permission model (chuẩn 2026)
az keyvault create \
  --name $KV_NAME \
  --resource-group $RG \
  --location $LOCATION \
  --enable-rbac-authorization true \
  --sku standard \
  --enable-soft-delete true \
  --retention-days 7

echo "Key Vault: $KV_NAME"

# Gán quyền Key Vault Secrets Officer cho user hiện tại (để tạo secret)
CURRENT_USER=$(az ad signed-in-user show --query id -o tsv)
KV_ID=$(az keyvault show --name $KV_NAME --query id -o tsv)

az role assignment create \
  --assignee $CURRENT_USER \
  --role "Key Vault Secrets Officer" \
  --scope $KV_ID

# Tạo secret trong Key Vault
sleep 15  # Đợi role assignment propagate
az keyvault secret set \
  --vault-name $KV_NAME \
  --name "db-connection-string" \
  --value "Server=sql-lab17.database.windows.net;Database=appdb;Encrypt=True"

# Tạo VM Ubuntu 22.04 LTS (Standard_B1s để tiết kiệm chi phí)
az vm create \
  --resource-group $RG \
  --name $VM_NAME \
  --image Ubuntu2204 \
  --size Standard_B1s \
  --location $LOCATION \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard \
  --tags "owner=hoatranlab" "env=lab" "module=az500"

echo "VM created: $VM_NAME"
VM_PUBLIC_IP=$(az vm show --resource-group $RG --name $VM_NAME \
  --show-details --query publicIps -o tsv)
echo "Public IP: $VM_PUBLIC_IP"
2

Bật System-assigned Managed Identity

Khi bật system-assigned MI, Azure tự tạo service principal trong Entra ID gắn với lifecycle của VM. Khi VM bị xóa, service principal cũng tự xóa.

Cách 1 — Portal
  1. 2.1Portal → tìm VM vm-az500-lab17 → menu trái → "Identity"
  2. 2.2Tab "System assigned" → Status: toggle sang On → click Save
  3. 2.3Xác nhận dialog → sau vài giây: Object ID xuất hiện — đây là Object ID của service principal
  4. 2.4Click "Azure role assignments" → quan sát hiện chưa có role nào được gán
Cách 2 — Azure CLI
Azure CLI— bật system-assigned MI và lấy Object ID
# Bật system-assigned managed identity cho VM
az vm identity assign \
  --resource-group $RG \
  --name $VM_NAME \
  --identities "[system]"

# Lấy Object ID của managed identity (service principal)
MI_OBJECT_ID=$(az vm identity show \
  --resource-group $RG \
  --name $VM_NAME \
  --query principalId -o tsv)

echo "Managed Identity Object ID: $MI_OBJECT_ID"

# Xác nhận trong Entra ID — service principal đã được tạo
az ad sp show --id $MI_OBJECT_ID \
  --query "{Name:displayName, Type:servicePrincipalType, AppId:appId}" \
  --output json
Kết quả (Output)— identity type ManagedIdentity
{
  "appId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "displayName": "vm-az500-lab17",
  "servicePrincipalType": "ManagedIdentity"
}
3

Gán Role Key Vault Secrets User (RBAC)

Gán built-in role Key Vault Secrets User cho managed identity của VM trên scope của Key Vault — chỉ cho phép đọc secret, không tạo/xóa được.

Cách 1 — Portal
  1. 3.1Portal → Key Vault kv-az500-lab17-* → menu trái → "Access control (IAM)"
  2. 3.2Click "+ Add""Add role assignment"
  3. 3.3Role: tìm "Key Vault Secrets User" → Next
  4. 3.4Members: "Managed identity""+ Select members" → Managed identity: "Virtual machine" → chọn vm-az500-lab17
  5. 3.5Review + assign → xác nhận role assignment thành công trong tab "Role assignments"
Cách 2 — Azure CLI
Azure CLI— gán Key Vault Secrets User cho MI
# Gán role "Key Vault Secrets User" cho managed identity trên scope Key Vault
az role assignment create \
  --assignee-object-id $MI_OBJECT_ID \
  --assignee-principal-type ServicePrincipal \
  --role "Key Vault Secrets User" \
  --scope $KV_ID

# Xác nhận role assignment
az role assignment list \
  --assignee $MI_OBJECT_ID \
  --scope $KV_ID \
  --output table

# Kết quả mong đợi:
# RoleDefinitionName        PrincipalName        Scope
# Key Vault Secrets User    vm-az500-lab17       /subscriptions/.../kv-az500-lab17-*
4

Lấy Token qua IMDS Endpoint

Azure Instance Metadata Service (IMDS) là HTTP endpoint nội bộ http://169.254.169.254 — chỉ truy cập được từ bên trong VM. Dùng để lấy access token cho managed identity mà không cần bất kỳ credential nào.

Cách 1 — Portal (Azure Bastion)

Portal → VM → ConnectBastion (nếu có) → SSH vào VM → chạy lệnh trong bước 4 CLI bên dưới

Cách 2 — Azure CLI + SSH vào VM
Bash (bên trong VM qua SSH)— IMDS token + Key Vault access
# === Chạy trên máy local — SSH vào VM ===
ssh azureuser@$VM_PUBLIC_IP

# === Từ đây tất cả lệnh chạy BÊN TRONG VM ===

# Cài curl nếu chưa có
sudo apt-get update && sudo apt-get install -y curl jq

# Lấy access token cho Key Vault qua IMDS
# resource = https://vault.azure.net (Key Vault endpoint)
TOKEN=$(curl -s \
  "http://169.254.169.254/metadata/identity/oauth2/token?\
api-version=2018-02-01&\
resource=https%3A%2F%2Fvault.azure.net" \
  -H "Metadata: true" | jq -r '.access_token')

echo "Token type: Bearer"
echo "Token (first 50 chars): ${TOKEN:0:50}..."

# Decode JWT header để xem thông tin token (không decode signature)
echo $TOKEN | cut -d'.' -f2 | base64 -d 2>/dev/null | jq \
  '{oid: .oid, appid: .appid, iss: .iss, exp: .exp}' 2>/dev/null || \
  echo "Token obtained successfully"
5

Đọc Secret từ Key Vault bằng Managed Identity

Dùng token vừa lấy qua IMDS để gọi Key Vault REST API — không có password, không có service principal credential nào trên VM.

Chạy bên trong VM (tiếp theo bước 4)
Bash (bên trong VM)— đọc secret từ Key Vault REST API
# Thay KV_NAME bằng tên Key Vault thực (hỏi admin hoặc xem từ Portal)
KV_NAME="kv-az500-lab17-XXXXX"  # Thay XXXXX bằng suffix thực

# Đọc secret "db-connection-string" từ Key Vault REST API
SECRET_RESPONSE=$(curl -s \
  "https://$KV_NAME.vault.azure.net/secrets/db-connection-string?api-version=7.4" \
  -H "Authorization: Bearer $TOKEN")

# Parse giá trị secret
SECRET_VALUE=$(echo $SECRET_RESPONSE | jq -r '.value')
echo "Secret value: $SECRET_VALUE"

# Kiểm tra metadata của secret
echo $SECRET_RESPONSE | jq '{id: .id, created: .attributes.created, enabled: .attributes.enabled}'

# Thử list tất cả secrets — sẽ FAIL vì Key Vault Secrets User không có quyền list
curl -s \
  "https://$KV_NAME.vault.azure.net/secrets?api-version=7.4" \
  -H "Authorization: Bearer $TOKEN" | jq '.error'
# Kết quả: Forbidden — đúng với principle of least privilege
Kết quả (Output)— đọc secret thành công
Secret value: Server=sql-lab17.database.windows.net;Database=appdb;Encrypt=True

{
  "id": "https://kv-az500-lab17-xxxxx.vault.azure.net/secrets/db-connection-string/...",
  "created": 1748000000,
  "enabled": true
}
Quan trọng: VM đọc được secret mà không có bất kỳ username/password/client-secret nào lưu trên disk. Token lấy qua IMDS tự động rotate bởi Azure — zero credential management overhead.

📊 Kết Quả Đầu Ra Lab 17

System-assigned MI được bật

VM Identity tab: Status = On, Object ID hiển thị, Entra ID có service principal ManagedIdentity type

RBAC role assigned

Key Vault IAM → Role assignments: vm-az500-lab17 có role "Key Vault Secrets User"

IMDS token lấy thành công

curl IMDS trả về access_token JWT hợp lệ, token type = Bearer

Secret đọc được từ Key Vault

REST API /secrets/db-connection-string trả về value đúng

Least privilege xác nhận

List secrets API trả về 403 Forbidden — Key Vault Secrets User không có quyền list

Zero credential on VM

Không có file .env, không có client_secret, không có connection string cứng trên VM

🧹 Dọn Dẹp

Azure CLI— xóa toàn bộ resource group
# Xóa toàn bộ resource group (VM, Key Vault, NIC, NSG, Public IP, Disk...)
az group delete \
  --name rg-az500-mi-lab17 \
  --yes \
  --no-wait

# Xóa soft-deleted Key Vault (nếu cần purge hoàn toàn để tái tạo với tên tương tự)
# az keyvault purge --name $KV_NAME --location $LOCATION

echo "Cleanup initiated. Resources will be deleted in background."

❓ Câu Hỏi Ôn Tập

1. IMDS endpoint 169.254.169.254 có thể truy cập từ bên ngoài VM không? Tại sao đây là cơ chế bảo mật quan trọng?

Gợi ý: Không — đây là link-local address, chỉ routable trong VM. Kẻ tấn công bên ngoài không thể lấy token, kể cả khi biết URL. Phải có code execution trên VM mới gọi được.

2. System-assigned MI bị xóa khi nào? So sánh với user-assigned MI về lifecycle và tái sử dụng.

Gợi ý: System-assigned MI xóa theo VM — không tái sử dụng được. User-assigned MI là independent resource — gán cho nhiều VM/App, tồn tại sau khi resource bị xóa.

3. Tại sao Key Vault nên dùng RBAC permission model thay vì Vault Access Policy (legacy)?

Gợi ý: RBAC có audit log tốt hơn, hỗ trợ scope (subscription/RG/vault/secret), tích hợp Azure Policy, dễ quản lý tập trung. Vault Access Policy legacy chỉ scope ở vault level.

4. Key Vault Secrets User khác Key Vault Secrets Officer ở điểm gì? Khi nào dùng mỗi role?

Gợi ý: Secrets User = chỉ GET và LIST secret versions (read-only). Secrets Officer = full CRUD. App đọc secret dùng Secrets User. Admin tạo/rotate secret dùng Secrets Officer.

5. Nếu VM bị compromise và kẻ tấn công chạy code bên trong, họ có thể dùng IMDS để đọc secret không? Làm thế nào để giảm thiểu rủi ro này?

Gợi ý: Có — đây là rủi ro real. Giảm thiểu: giới hạn secret scope (chỉ đúng secret cần thiết), bật Private Endpoint cho Key Vault, giám sát KV access log, dùng Defender for Key Vault để alert truy cập bất thường.

Lab 16: OAuth Permission Grant Thư viện Labs Lab 18: User-assigned MI cho App Service
Zalo