🎯 Mục Tiêu Lab
Tạo VNet với subnet AzureBastionSubnet đúng prefix /26
Tạo VM không có public IP, chỉ có private IP
Triển khai Azure Bastion Standard SKU
Kết nối RDP vào VM qua Bastion từ trình duyệt
Xác nhận NSG không cần mở port 3389 từ Internet
Dọn dẹp tài nguyên để tiết kiệm chi phí
📋 Chuẩn Bị
- Azure subscription với quyền Contributor trở lên
- Azure CLI phiên bản 2.50+ hoặc truy cập Azure Portal
- Region: southeastasia (Singapore)
- RG: rg-az500-lab32
- VNet: vnet-az500-bastion
- VM: vm-az500-lab32
- Bastion: bastion-az500-lab32
🏗️ Kịch Bản
Doanh nghiệp yêu cầu các VM quản trị không được có public IP. Quản trị viên phải kết nối RDP/SSH qua Azure Bastion — một dịch vụ managed PaaS hoạt động trong subnet riêng của VNet, xác thực qua Azure Portal/Entra ID, truyền tải mã hóa TLS qua port 443. Với SKU Standard, Bastion hỗ trợ native client (az network bastion tunnel), file upload/download và IP-based connection.
🧪 Các Bước Thực Hiện
Tạo Resource Group và VNet
- 1.1Portal → Resource Groups → + Create → Name:
rg-az500-lab32→ Region: Southeast Asia → Review + Create - 1.2Portal → Virtual Networks → + Create → Name:
vnet-az500-bastion→ Region: Southeast Asia → Address space:10.10.0.0/16 - 1.3Tab Subnets → Xóa subnet mặc định → Add subnet: Name
snet-workload, prefix10.10.1.0/24 - 1.4Thêm subnet thứ hai: Name
AzureBastionSubnet(tên chính xác), prefix10.10.2.0/26→ Review + Create
# Tạo resource group
az group create \
--name rg-az500-lab32 \
--location southeastasia \
--tags "lab=32" "course=az500" "owner=hoatranlab"
# Tạo VNet với hai subnet
az network vnet create \
--resource-group rg-az500-lab32 \
--name vnet-az500-bastion \
--address-prefix 10.10.0.0/16 \
--subnet-name snet-workload \
--subnet-prefix 10.10.1.0/24
# Thêm AzureBastionSubnet (tên PHẢI chính xác)
az network vnet subnet create \
--resource-group rg-az500-lab32 \
--vnet-name vnet-az500-bastion \
--name AzureBastionSubnet \
--address-prefix 10.10.2.0/26
Tạo VM Không Có Public IP
- 2.1Portal → Virtual Machines → + Create → Name:
vm-az500-lab32→ Region: Southeast Asia → Image: Windows Server 2022 → Size: Standard_B2s - 2.2Tab Networking → VNet:
vnet-az500-bastion→ Subnet:snet-workload→ Public IP: None → NIC NSG: Basic - 2.3Ghi nhớ username/password admin → Review + Create → Create
# Tạo VM Windows Server 2022 không có public IP
az vm create \
--resource-group rg-az500-lab32 \
--name vm-az500-lab32 \
--image Win2022AzureEditionCore \
--size Standard_B2s \
--vnet-name vnet-az500-bastion \
--subnet snet-workload \
--public-ip-address "" \
--nsg-rule NONE \
--admin-username azureadmin \
--admin-password "P@ssw0rd!Az500Lab32" \
--location southeastasia
# Xác nhận VM không có public IP
az vm show \
--resource-group rg-az500-lab32 \
--name vm-az500-lab32 \
--query "networkProfile.networkInterfaces[0].id" -o tsv | xargs az network nic show --ids | \
grep -E "publicIPAddress|privateIPAddress"
Triển Khai Azure Bastion Standard
- 3.1Portal → Bastions → + Create → RG:
rg-az500-lab32→ Name:bastion-az500-lab32 - 3.2Region: Southeast Asia → Tier: Standard → VNet:
vnet-az500-bastion - 3.3Public IP: Create new → Name:
pip-bastion-lab32→ SKU: Standard → Review + Create - 3.4Đợi provisioning ~5-10 phút cho đến khi status = Succeeded
# Tạo Public IP cho Bastion (bắt buộc Standard SKU)
az network public-ip create \
--resource-group rg-az500-lab32 \
--name pip-bastion-lab32 \
--sku Standard \
--location southeastasia
# Deploy Azure Bastion Standard SKU
az network bastion create \
--resource-group rg-az500-lab32 \
--name bastion-az500-lab32 \
--public-ip-address pip-bastion-lab32 \
--vnet-name vnet-az500-bastion \
--location southeastasia \
--sku Standard \
--enable-tunneling true
# Kiểm tra trạng thái (đợi provisioningState = Succeeded)
az network bastion show \
--resource-group rg-az500-lab32 \
--name bastion-az500-lab32 \
--query "{name:name, sku:sku.name, state:provisioningState}" -o table
Kết Nối RDP Qua Bastion
- 4.1Portal → Virtual Machines →
vm-az500-lab32→ Connect → Bastion - 4.2Nhập username:
azureadminvà password đã đặt ở bước 2 - 4.3Click Connect — cửa sổ RDP mở trong trình duyệt (tab mới) qua HTTPS port 443
- 4.4Bên trong VM: mở PowerShell → chạy
ipconfig→ xác nhận chỉ có private IP 10.10.1.x
# Lấy VM resource ID
VM_ID=$(az vm show \
--resource-group rg-az500-lab32 \
--name vm-az500-lab32 \
--query id -o tsv)
# Tạo tunnel qua Bastion (chạy terminal riêng, giữ mở)
az network bastion tunnel \
--name bastion-az500-lab32 \
--resource-group rg-az500-lab32 \
--target-resource-id $VM_ID \
--resource-port 3389 \
--port 5000
# Trong terminal khác: kết nối bằng mstsc hoặc xfreerdp
# Windows:
mstsc /v:localhost:5000
# Linux/macOS:
xfreerdp /v:localhost:5000 /u:azureadmin
ipconfig trong VM → chỉ thấy IP 10.10.1.x, không có public IP nào.
Xác Minh VM Không Expose Port Ra Internet
# Xác nhận NIC không có public IP
NIC_ID=$(az vm show -g rg-az500-lab32 -n vm-az500-lab32 \
--query "networkProfile.networkInterfaces[0].id" -o tsv)
az network nic show --ids $NIC_ID \
--query "ipConfigurations[0].{privateIP:privateIPAddress, publicIP:publicIPAddress}" -o table
# Kiểm tra NSG rules — không có rule mở port 3389 từ Internet
NSG_ID=$(az network nic show --ids $NIC_ID \
--query "networkSecurityGroup.id" -o tsv)
az network nsg show --ids $NSG_ID \
--query "securityRules[?destinationPortRange=='3389']" -o table
# Dùng Network Watcher IP Flow Verify để xác nhận block
az network watcher test-ip-flow \
--direction Inbound \
--protocol TCP \
--local 10.10.1.4:3389 \
--remote 1.2.3.4:12345 \
--vm vm-az500-lab32 \
--resource-group rg-az500-lab32
📊 Kết Quả Đầu Ra
az network vnet subnet list → thấy AzureBastionSubnet với prefix 10.10.2.0/26
az network nic show → publicIPAddress: null, privateIPAddress: 10.10.1.x
az network bastion show → provisioningState: Succeeded, sku: Standard
Màn hình desktop VM hiển thị trong trình duyệt, ipconfig chỉ thấy 10.10.1.x
test-ip-flow trả về Access: Deny cho inbound port 3389 từ Internet
🧹 Dọn Dẹp Tài Nguyên
# Xóa toàn bộ resource group (bao gồm Bastion, VM, VNet, Public IP)
az group delete \
--name rg-az500-lab32 \
--yes \
--no-wait
# Xác nhận đã xóa
az group show --name rg-az500-lab32 2>&1 || echo "Resource group đã được xóa thành công"
❓ Câu Hỏi Ôn Tập
1. AzureBastionSubnet yêu cầu prefix tối thiểu là gì? Tên subnet có thể tùy ý đặt không?
Gợi ý: /26 (64 địa chỉ). Tên phải là chính xác "AzureBastionSubnet" — không thể đổi tên.
2. Sự khác nhau giữa Bastion Basic và Standard SKU là gì?
Gợi ý: Standard hỗ trợ native client tunneling, file copy, IP-based connection, Kerberos auth, shareable links. Basic chỉ có browser-based RDP/SSH.
3. Bastion sử dụng port nào để truyền dữ liệu? Tại sao điều này thuận lợi hơn mở port 3389?
Gợi ý: Port 443 (HTTPS/TLS). Hầu hết corporate firewall cho phép outbound 443. Port 3389 thường bị chặn và là mục tiêu tấn công brute force.
4. Khi dùng Bastion, VM cần NSG rule inbound cho port 3389 không? Tại sao?
Gợi ý: Không cần. Bastion kết nối đến VM qua private IP nội bộ VNet. Bastion có NSG riêng yêu cầu inbound 443 từ Internet và outbound 3389/22 đến VNet.
5. Trong mô hình Zero Trust, tại sao Bastion được ưa dùng thay cho VPN + Jump Server truyền thống?
Gợi ý: Bastion tích hợp Entra ID authentication, không cần quản lý thêm VM jump server, session được audit, không cần mở port từ Internet, mỗi phiên là TLS riêng biệt.