Episode 2 — Virtual Machines: Anatomy and Provisioning
In Episode 1 — The Azure Compute Landscape: Choosing the Right Service, you learned when a VM is the right tool for the job. Now we open the box: what a VM deployment actually creates, how to read a size name at a glance, and how to provision machines like a professional instead of clicking "Next" seven times.
Learning objectives
- Explain the five resources a typical VM deployment creates and how they relate.
- Decode any VM size name (family, vCPUs, features, generation) and pick a family for a workload.
- Choose the right image source: Marketplace, custom image, or Azure Compute Gallery — generalized vs specialized.
- Provision a VM with
az vm create, using SSH keys and sensible defaults. - Distinguish Stopped from Stopped (deallocated) and predict the billing consequences.
Why it matters
A VM is never just "a VM." Every deployment silently creates a small graph of resources — disk, NIC, IP, security group — and each one has its own lifecycle, cost, and security implications. Admins who don't understand this anatomy end up with orphaned disks that bill forever, public IPs nobody remembers opening, and VMs that are "off" but still charging compute by the hour. On the AZ-104 exam, size-name decoding, image types, and the stopped-vs-deallocated distinction are all classic question territory. In real life, they're the difference between a tidy, predictable estate and a junk drawer.
Anatomy: what a VM deployment really creates
When you deploy a single VM, Azure creates (or expects you to supply) a set of separate, first-class resources that are wired together. The VM object itself is mostly compute configuration — size, image reference, admin account. Everything else lives beside it.
The core cast:
- The VM resource — the compute definition: size, image, availability options, extensions.
- The OS disk — a managed disk created from the image. It survives independently: delete the VM and, by default behavior you should always verify, the disk can outlive it.
- The NIC (network interface) — the VM's attachment point to a subnet in a virtual network. A VM must have at least one NIC.
- A public IP address (optional) — attached to the NIC if you want direct inbound reachability. Many production VMs correctly have none.
- A network security group (NSG) — a stateful firewall of allow/deny rules, associated at the NIC or subnet level (or both).
Why does this matter operationally? Because each resource is managed and billed separately. You can detach a NIC, swap an OS disk, or delete a VM while keeping its disk for forensics. The flip side: cleanup is your job. Deleting a VM in a hurry and leaving its disk, NIC, and public IP behind is the most common source of cloud clutter. (Delete-with-VM options exist — but always check what a deletion actually removed.)
VM sizes: decoding the name
Azure has hundreds of VM sizes, but the naming scheme makes them readable. Take Standard_D4s_v5:
| Fragment | Meaning |
|---|---|
D | Family — general purpose |
4 | vCPU count |
s | Premium-storage capable (can attach Premium SSD) |
v5 | Generation of the family |
The family letter is the part worth memorizing, because it tells you the CPU-to-memory ratio and specialty:
| Family | Optimized for | Typical workload |
|---|---|---|
| B | Burstable (credits) | Dev/test, low-traffic sites |
| D | General purpose | Web servers, small databases |
| E | Memory-optimized | Caches, in-memory analytics |
| F | Compute-optimized | Batch, gaming servers, CI agents |
| L | Storage-optimized | NoSQL, data warehousing |
| N | GPU | ML training/inference, rendering |
| M | Very large memory | SAP HANA, giant databases |
Memory hook — the letter says what it does: Burstable, Default (general purpose), Extra memory, Fast CPU, Lots of local disk, NVIDIA GPU, Massive memory. Read the letter, hear the meaning.
Two more lowercase letters you'll meet often: d means the size includes a local temp disk (in newer generations, sizes without d often have no temp disk at all), and a or p indicate AMD or ARM-based processors. Exact per-size specs (IOPS caps, memory figures) change between generations — check current limits rather than memorizing numbers.
One architecture note: the s matters beyond storage speed. The single-VM SLA of ≥ 99.9% applies only when all disks are Premium SSD or Ultra — which requires a premium-storage-capable (s) size in the first place.
Exercise 2.1 — Decode two size names
You inherit two VMs: Standard_E8s_v5 and Standard_F4. Decode both names, and state which one could qualify for the single-VM 99.9% SLA (assuming appropriate disks).
Solution: Work left to right. Standard_E8s_v5: E = memory-optimized family, 8 vCPUs, s = premium-storage capable, v5 = fifth generation. Suited to memory-hungry workloads like caches or analytics. Standard_F4: F = compute-optimized, 4 vCPUs, no s = cannot attach Premium SSD, no version suffix = the family's original generation. Suited to CPU-bound batch work. Only the E8s_v5 can qualify for the single-VM SLA: the 99.9% figure requires Premium SSD or Ultra on all disks, and the F4 can't attach premium storage at all.
Images: where the OS comes from
Every VM starts from an image. You have three sources, in increasing order of ownership:
- Azure Marketplace — Microsoft- and partner-published images (Ubuntu, Windows Server, RHEL, appliances). Zero maintenance for you; you customize after boot.
- Custom images — you build a VM, configure it, and capture it. You now own patching and rebuilding that image.
- Azure Compute Gallery — the professional home for custom images: versioning, replication across regions, and sharing across subscriptions and tenants. If more than one team or region consumes your image, it belongs here.
Whatever the source, an image is either generalized or specialized — and confusing the two is a classic provisioning failure:
| Generalized | Specialized | |
|---|---|---|
| Preparation | Sysprep (Windows) / waagent -deprovision (Linux) | None |
| Machine identity | Stripped — hostname, accounts, SIDs removed | Kept as-is |
| At deployment | Azure runs provisioning: you supply new hostname + admin credentials | Boots as a clone; no provisioning step |
| Best for | Golden image stamped into many VMs | Rescue copy, migration, one-off clone |
The rule of thumb: many VMs from one template → generalized; an exact copy of this one machine → specialized. Deploying a specialized image twice into the same network also means duplicate hostnames — plan for it.
Provisioning: portal vs CLI
The portal wizard walks you through the same anatomy you saw above — basics (size, image, auth), disks, networking, management — and is fine for one-off or exploratory deployments. The problem is repeatability: nobody remembers which of the forty checkboxes they ticked last month.
The Azure CLI makes the deployment a reviewable artifact. A minimal, honest example:
az vm create \
--resource-group myRG \
--name myVM \
--location westeurope \
--image Ubuntu2204 \
--size Standard_D2s_v5 \
--admin-username azureuser \
--generate-ssh-keysKnow what the defaults do: if you don't specify them, az vm create creates a VNet, subnet, public IP, and NSG for you. Wonderful for a lab, wrong for production — there you pass your existing network (--vnet-name, --subnet) and suppress the public IP with --public-ip-address "". Convenience defaults are how accidental internet exposure happens.
Authentication: SSH keys vs passwords
For Linux, this is barely a debate: SSH keys are the default and the right answer. A key is not guessable, is never typed over the wire, and --generate-ssh-keys creates or reuses a local key pair transparently. Passwords remain for Windows (RDP), where you should compensate with strong secrets and no public exposure — Episode 3 shows how Bastion and just-in-time access remove the exposed port entirely.
| SSH keys | Passwords | |
|---|---|---|
| Brute-forceable | Effectively no | Yes |
| Typical use | Linux default | Windows/RDP |
| Rotation | Replace public key | Change secret everywhere |
Exercise 2.2 — Fix this az vm create
A colleague's command fails, and even once it runs, the design won't meet their stated goal of the single-VM 99.9% SLA. Find both problems:
az vm create \
--resource-group myRG \
--name myVM \
--image Ubuntu2204 \
--size Standard_D4_v5 \
--admin-username azureuser \
--generate-ssh-keys \
--admin-password "P@ssw0rd123!"Solution: Problem 1 — conflicting authentication: --generate-ssh-keys and --admin-password specify two auth methods; for Linux, drop the password and keep the keys. Problem 2 — the size can't carry the SLA: Standard_D4_v5 has no s, so it cannot attach Premium SSD, and the single-VM ≥ 99.9% SLA requires Premium SSD or Ultra on all disks. Change to --size Standard_D4s_v5 and ensure the OS disk is Premium. Corrected command: same as above minus --admin-password, with --size Standard_D4s_v5.
The VM Agent and extensions
Inside every Azure-created VM runs the VM Agent (Windows Guest Agent / walinuxagent), the platform's hands inside the guest OS. It powers extensions — packaged in-guest operations you trigger from outside:
- Custom Script Extension — downloads and runs a script; the standard way to bootstrap software after deployment.
- Run Command — sends a script through the Azure control plane, so it works even when the network path (NSG, lost SSH key) is broken. A lifesaver for lockouts:
az vm run-command invoke \
--resource-group myRG \
--name myVM \
--command-id RunShellScript \
--scripts "systemctl status nginx"For Linux first-boot configuration, prefer cloud-init: a declarative file processed during the initial boot, earlier and more idempotently than a bolted-on script:
#cloud-config
package_update: true
packages:
- nginx
runcmd:
- systemctl enable --now nginxPass it at creation time with az vm create ... --custom-data cloud-init.yaml. Rule of thumb: cloud-init for birth, extensions for life — initial configuration in cloud-init, later interventions via extensions or Run Command.
Power states and billing: stopped is not deallocated
The single most expensive misunderstanding in VM administration: shutting down the OS does not stop the meter. Azure only releases the compute hardware — and stops charging for it — when the VM is deallocated.
| State | How you get there | Compute billed? | Disks / IP billed? |
|---|---|---|---|
| Running | az vm start | Yes | Yes |
| Stopped | Shutdown inside the guest OS, or az vm stop | Yes | Yes |
| Stopped (deallocated) | az vm deallocate, or the portal Stop button | No | Yes (disks; a static public IP still bills) |
az vm stop --resource-group myRG --name myVM # guest is off, compute STILL billed
az vm deallocate --resource-group myRG --name myVM # compute billing stopsDeallocation has side effects, because the VM leaves its physical host: the temp disk is wiped (more on that in Episode 3), and a dynamic public IP may change on the next start. And note the last column: even deallocated, managed disks keep billing — a "stopped" estate is cheaper, not free.
Exercise 2.3 — The Friday-night shutdown
On Friday evening an admin RDPs into a Windows VM and shuts it down from the Start menu. On Monday, the cost report still shows compute charges for the whole weekend. Explain why, and give the command that would have prevented it. Would the bill have dropped to zero?
Solution: A guest-OS shutdown puts the VM in Stopped — the hardware allocation on the host is retained, so compute billing continues. The admin needed to deallocate: az vm deallocate --resource-group myRG --name myVM (or the portal's Stop button, which deallocates). Even then the bill is not zero: the OS disk and any data disks keep billing, as does a static public IP. Two caveats worth stating in the runbook: deallocation wipes the temp disk and a dynamic public IP may change on restart — so services pointing at that IP need a static IP or a DNS name.
Key takeaways
- A VM deployment is a resource graph: VM + OS disk + NIC + optional public IP + NSG, each with its own lifecycle and bill.
az vm createinvents network resources by default — great for labs, dangerous defaults for production.- Size names are readable: family letter, vCPU count, feature letters (
s,d), generation — and onlyssizes can carry the single-VM 99.9% SLA (with Premium SSD/Ultra). - Generalized images stamp out many machines; specialized images clone exactly one.
- Azure Compute Gallery is where serious custom images live: versioned, replicated, shared.
- SSH keys over passwords on Linux, always; never rely on an exposed port either way.
- cloud-init for birth, extensions for life; Run Command works even when the network path doesn't.
- Stopped still bills compute; Stopped (deallocated) doesn't — but disks and static IPs bill regardless.
Quick recall
Q: Name the five resources a typical single-VM deployment involves. A: The VM itself, a managed OS disk, a NIC, an optional public IP, and an NSG (at NIC or subnet level).
Q: Decode Standard_D4s_v5.
A: D family (general purpose), 4 vCPUs, s = premium-storage capable, v5 = fifth generation.
Q: Generalized vs specialized image in one line each? A: Generalized = identity stripped (Sysprep/waagent), template for many VMs. Specialized = identity kept, exact clone of one VM.
Q: Which power state stops compute billing, and what still bills? A: Stopped (deallocated). Managed disks and static public IPs continue to bill.
Q: Your NSG rules lock you out of SSH. How do you still run a command in the guest?
A: Run Command (az vm run-command invoke) — it goes through the Azure control plane via the VM Agent, not the network path.
Q: When do you use cloud-init vs the Custom Script Extension? A: cloud-init for declarative first-boot setup on Linux; Custom Script Extension (or Run Command) for changes after the VM exists.
Coming up
Your VM is running — but its disks are still a mystery, its management port is a liability, and one day you'll need to resize it without breaking anything. In Episode 3 — VM Disks, Security, and Day-2 Administration, we dissect OS, temp, and data disks, compare the managed disk types, lock down access with Bastion and just-in-time, and cover the operations that fill an admin's actual week.