Lesson 10 — VM Scale Sets, Azure Bastion & JIT Access
Why This Lesson Completes the Compute Domain
Lesson 09 covered individual VM configuration. This lesson covers the three capabilities that matter most once you have VMs running in production: scaling them (VM Scale Sets), accessing them securely without exposing attack surface (Azure Bastion), and controlling emergency access to management ports (Just-In-Time VM Access). Together, these are the difference between a VM deployment that works and one that is production-grade.
On AZ-104, VMSS questions focus on orchestration modes and autoscale policies. Bastion questions focus on subnet requirements and SKU differences. JIT questions focus on the NSG mechanics. All three are regularly tested in scenario-based questions — know the operational details, not just the names.
VM Scale Sets — Overview
A VM Scale Set (VMSS) is an Azure resource that lets you deploy and manage a group of load-balanced VMs as a single unit. Scale sets solve the problem of manual fleet management: instead of deploying individual VMs, configuring them, and wiring them to a load balancer manually, a scale set handles instance provisioning, configuration, distribution across fault domains and availability zones, health monitoring, and autoscaling as a first-class feature.
- All instances in a scale set are created from the same base image and configuration (Uniform mode) or from a model with per-instance overrides (Flexible mode).
- Scale sets integrate natively with Azure Load Balancer (Layer 4) and Application Gateway (Layer 7) backend pools.
- Scale sets support Availability Zones — instances can be distributed across zones for high availability with no extra configuration.
Orchestration Modes: Uniform vs. Flexible
This is one of the most commonly tested VMSS topics on AZ-104. The two orchestration modes represent fundamentally different philosophies.
| Aspect | Uniform Mode | Flexible Mode |
|---|---|---|
| Instance uniformity | All instances are identical — same OS image, same VM size, same configuration. Azure manages instance profiles directly. | Instances can have different configurations and sizes. Each instance is a full first-class VM resource. |
| AZ distribution | Supports zone spanning, but limited to a single zone or zone balancing. | Full Availability Zone mixing within the same scale set — instances can be in Zone 1, 2, and 3 simultaneously with fine-grained control. |
| Autoscale | Supported. | Supported. |
| Maximum instances | 1,000 (with Azure platform images); 600 (with custom images) | 1,000 |
| Instance visibility in portal | Instances appear as scale set instances — partial VM resource visibility | Each instance appears as a full independent VM in the portal and ARM — full VM resource lifecycle |
| Microsoft recommendation | Legacy — still supported for existing deployments | Preferred for all new deployments |
Scaling Policies
VMSS supports four distinct scaling approaches, which can be combined in a single scale set configuration:
1. Manual Scaling
Set the instance count explicitly via the portal, CLI, or API. No automation — you directly control the fleet size. Useful for predictable workloads or initial deployment.
2. Custom Autoscale (Metric-Based)
Define scale-out and scale-in rules based on metrics. A complete autoscale profile consists of:
- Scale-out rule: When metric X exceeds threshold Y for Z minutes, add N instances.
- Scale-in rule: When metric X falls below threshold W for Z minutes, remove N instances.
- Cooldown period: A waiting period after a scaling operation before another can trigger — prevents thrashing (rapid scale-in/scale-out cycles). Typical values: 5–10 minutes.
- Min/Max bounds: Hard floor and ceiling on instance count.
| Metric Source | Examples |
|---|---|
| VM host metrics | CPU percentage, network in/out, disk I/O |
| Azure Monitor custom metrics | Application-emitted metrics (request queue depth, active sessions) |
| Storage Queue messages | Scale based on queue depth — useful for background worker patterns |
| Service Bus queue/topic | Scale based on active message count |
3. Schedule-Based Scaling
Increase or decrease capacity on a defined schedule — for example, scale to 20 instances at 08:00 Monday–Friday and scale back to 5 at 18:00. Useful for predictable business-hours load patterns.
4. Predictive Autoscale
Predictive autoscale uses Azure Machine Learning to analyse CPU usage history and predict future demand. Instead of reacting to a metric breach, it pre-provisions capacity before the demand spike hits — eliminating the latency gap between load increase and new instance readiness that standard reactive autoscale cannot avoid.
Scale-In Policy
When a scale-in event fires, Azure must decide which VM instances to delete. The scale-in policy controls this:
- Default: Balances instance removal across Availability Zones and Fault Domains first, then deletes the VM with the highest instance ID. This is the safest option for AZ-balanced deployments.
- NewestVM: Deletes the most recently created instances first — good for immutable infrastructure patterns where newer instances are less likely to have in-flight work.
- OldestVM: Deletes the oldest instances first — good for patching workflows where older instances may be running older software versions.
Instance Protection
You can mark specific VMSS instances as protected to prevent them from being deleted during scale-in or from receiving model updates (OS upgrades, configuration changes). Useful for instances running long-running batch jobs that should not be interrupted mid-job.
Updates, Health Probes & Rolling Upgrades
Keeping a VMSS fleet updated without downtime requires understanding the upgrade mode and health probe configuration:
| Upgrade Mode | Behaviour | Risk |
|---|---|---|
| Manual | New VMSS model changes do not automatically apply to existing instances. You must manually trigger an upgrade per instance. | Low (you control when) |
| Automatic | Azure immediately applies new model changes to all instances. Can cause simultaneous reboots if not configured carefully. | High if health probes not configured |
| Rolling | Azure upgrades instances in batches (configurable batch size and pause duration). Waits for health probes to pass before proceeding to the next batch. | Low — zero-downtime rolling update |
For zero-downtime updates, configure a health probe (via Application Health Extension or Load Balancer probe) so Azure knows whether an instance is healthy before upgrading the next batch. Automatic OS image upgrades can also be enabled to automatically replace instances with the latest OS image version on a rolling basis.
Azure Bastion
Azure Bastion solves a fundamental enterprise security problem: how do you remotely administer VMs over RDP or SSH without exposing those ports to the public internet?
The Problem Without Bastion
Traditional approaches all have serious flaws:
- Assign a public IP to the VM and open port 3389/22 in the NSG — exposes the VM directly to internet scanning and brute force attacks.
- Use a jump box VM with a public IP — you still have a publicly exposed attack surface (the jump box itself).
- Use a VPN — requires VPN infrastructure, client software distribution, and ongoing management overhead.
How Azure Bastion Works
Azure Bastion is a fully managed PaaS service that proxies RDP and SSH connections through the Azure infrastructure, entirely within Azure's backbone network:
Azure Bastion SKUs
| SKU | Connection Method | Peered VNets | Key Features |
|---|---|---|---|
| Basic | Browser-based (Azure portal HTML5 client) | No | Core RDP/SSH. No native client support. Only VMs in the same VNet. |
| Standard | Browser-based + Native client (RDP/SSH apps) | Yes (peered VNets) | Native client (full RDP/SSH feature set), IP-based connection (connect by private IP without Azure resource ID), shareable links, tunneling, file transfer, custom port support. |
| Premium | Browser-based + Native client | Yes | All Standard features plus: session recording, private-only deployment (no public IP on Bastion itself), enhanced security features. |
Deployment Requirements
Azure Bastion has specific infrastructure requirements that are frequently tested:
AzureBastionSubnet — not "bastion-subnet", not "AzureBastion", not "BastionSubnet". If the name is wrong, Bastion deployment will fail. The CIDR must be at minimum /26 (64 addresses) because Bastion provisions multiple instances internally for availability — a /27 is too small and will be rejected. No other resources (VMs, NVAs, etc.) may be deployed into this subnet.
Just-In-Time (JIT) VM Access
Just-In-Time VM Access is a feature of Microsoft Defender for Servers (Plan 1 or Plan 2 required). It solves the persistent management port exposure problem using a different mechanism than Bastion.
The Problem JIT Solves
Even with disciplined NSG management, many teams leave RDP (3389) or SSH (22) open permanently in their NSGs — because removing access means re-adding it every time someone needs to do maintenance, which creates operational friction. Permanently open management ports are a significant attack surface: every IP on the internet can attempt brute-force or vulnerability attacks against them continuously.
How JIT Works — NSG Mechanics
| Aspect | Details |
|---|---|
| Requires | Microsoft Defender for Servers Plan 1 or Plan 2 enabled on the subscription or VM resource group |
| Max access duration | 3 hours per JIT request |
| Source IP | Requester can specify their own public IP (most secure) or a CIDR range |
| Audit trail | All requests and approvals logged in Azure Activity Log and Defender for Cloud |
| Supported ports | Configurable — defaults cover RDP (3389), SSH (22), WinRM (5985, 5986) |
Bastion vs. JIT — Which to Use?
Both Azure Bastion and JIT VM Access address the same problem (secure management access) but through fundamentally different mechanisms. Understanding when to recommend each is important:
| Aspect | Azure Bastion | JIT VM Access |
|---|---|---|
| VM public IP required? | No — VM needs no public IP | Yes (for direct connectivity) or accessible via VPN/ExpressRoute |
| Port 3389/22 open in NSG? | No — VM NSG does not need RDP/SSH open from internet | Temporarily opened for requester's IP only, then closed |
| Requires separate service? | Yes — Azure Bastion PaaS deployed in VNet | Yes — Microsoft Defender for Servers |
| Connection method | Azure portal or native client via HTTPS to Bastion endpoint | Direct RDP/SSH client to VM public IP (after JIT access granted) |
| Modern recommendation | Yes — preferred for zero-public-IP architectures | Yes — for scenarios requiring direct IP connectivity (legacy tools, scripted access) |
Azure Serial Console
Azure Serial Console is an emergency access mechanism that provides a text-only, browser-based terminal connected to a VM's serial port — bypassing all network connectivity entirely. It is your last resort when everything else is broken.
| Aspect | Details |
|---|---|
| Access method | Azure portal → VM → Serial Console (under Support + Troubleshooting) |
| Network dependency | None — connects via Azure management plane, not VM's NIC or public IP |
| Requires | Boot diagnostics enabled on the VM (requires a storage account or Azure Managed Boot Diagnostics) |
| OS access | Text-mode console at the OS level — can access boot menus (GRUB on Linux), SAC (Special Administration Console on Windows), emergency shell |
| Use cases | Fix broken network configuration, recover from failed OS updates, reset passwords via single-user mode, diagnose VMs stuck in boot loop, access Linux rescue shell |
Check Your Understanding
Click any option to see immediate feedback. Answers represent correct behaviour in a real Azure environment.
1. You are creating a new VM Scale Set for a stateless web tier that requires Availability Zone distribution, mixed VM configurations per instance, and standard autoscaling. Which orchestration mode should you choose?
2. You are deploying Azure Bastion into an existing VNet. The VNet has a subnet named "bastion-hosts" with a /27 CIDR block. You attempt to deploy Bastion into this subnet but the deployment fails. What are the two issues?
AzureBastionSubnet — Azure performs a strict string match; "bastion-hosts" will fail. Second, the subnet must be at minimum /26 (64 addresses). A /27 provides only 32 addresses, which is insufficient for Bastion's internal multi-instance deployment. Both conditions must be corrected before the deployment will succeed. The subnet must be in the same VNet as the VMs (for Basic SKU) or in a VNet peered to the target VNets (for Standard/Premium SKU).3. A security engineer asks how JIT VM Access prevents unauthorised access to management ports between approved access windows. What is the technically accurate description?
4. Your organisation uses a hub-and-spoke VNet topology. Azure Bastion is deployed in the hub VNet. Spoke VNet A and Spoke VNet B are both peered to the hub. You need to connect to VMs in Spoke VNet A using Bastion. What SKU is required?
5. Your VMSS-based web tier experiences daily CPU spikes at 09:00 as business hours begin. Standard metric-based autoscale takes 5–8 minutes to respond, during which users experience degraded performance. What feature eliminates this latency gap?
6. A Linux VM in a production VNet has a broken network interface configuration — the NIC IP address was changed incorrectly, and SSH is unreachable. No public IP is attached. No Azure Bastion is deployed. What is the correct Azure-native emergency access method?
Also read the Azure Bastion documentation and the JIT VM Access documentation under Microsoft Defender for Cloud. The Bastion FAQ and the JIT "how it works" page are particularly useful for exam preparation.
This lesson covered VMSS, Bastion, and JIT. Go deeper with any of these:
- Walk me through configuring a VMSS with predictive autoscale, a custom scale-out rule based on queue depth, and a rolling upgrade policy.
- How does Azure Bastion native client mode work — what ports and protocols does it use on the client machine?
- What are the RBAC permissions required to approve a JIT access request, vs. to configure JIT policy on a VM?
- How do you use Azure Serial Console to recover a Linux VM with a corrupted GRUB bootloader configuration?