The Portal splits VM creation into tabs: Basics (subscription, RG, name, region, image, size, admin, inbound ports), Disks, Networking (VNet, subnet, public IP, NSG, load balancing), Management (monitoring, auto-shutdown, backup), Advanced (extensions, cloud-init/custom data).
az vm create -g rg-devops-lab -n vm-web --image Ubuntu2204 --size Standard_B1s --admin-username azureuser --generate-ssh-keys
--generate-ssh-keys uses (or creates) a key pair in ~/.ssh/; the private key stays on your machine and the public key goes into the VM. Lose the private key and you cannot SSH in any more (reset it through the VMAccess extension).
flowchart LR L["Laptop"] -- "HTTPS 443" --> B["Azure Bastion<br/>(AzureBastionSubnet)"] B -- "internal SSH/RDP" --> V["vm-web (no public IP)"] L -. "SSH 22 over the Internet (restrict)" .-> P["vm-web public IP"]
| Connection | Port | Notes |
|---|---|---|
| SSH (Linux) | TCP 22 | Keys instead of passwords |
| RDP (Windows) | TCP 3389 | Graphical desktop |
| WinRM | 5985/5986 | Remote commands/scripts on Windows |
| Azure Bastion | 443 through the Portal | No public IP on the VM; needs a subnet named AzureBastionSubnet |
Traps: opening 22/3389 to the whole Internet; mixing up RDP 3389 and SSH 22. az vm open-port adds a rule to the VM's NSG — open only the ports the app needs (80, say).
| Command | Meaning |
|---|---|
az vm open-port -g rg-devops-lab -n vm-web --port 80 |
Open HTTP |
az vm list-ip-addresses -g rg-devops-lab -n vm-web -o table |
Show public/private IPs |
ssh azureuser@203.0.113.20 |
Connect |
Images
--image Ubuntu2204 is an alias for a full URN in the form Publisher:Offer:Sku:Version, for example Canonical:0001-com-ubuntu-server-jammy:22_04-lts-gen2:latest. az vm image list --publisher Canonical --offer 0001-com-ubuntu-server-jammy --all -o table lists versions. Your own golden images go into an Azure Compute Gallery, which versions them and replicates them to other regions.
Configuring the VM at first boot
Custom data passes a cloud-init file that runs at first boot, a simple way to install packages:
#cloud-config
package_update: true
packages:
- nginx
Use it with az vm create ... --custom-data cloud-init.yaml. VM extensions add software after creation, such as the Custom Script extension or the Azure Monitor Agent.
VM states and billing
| State | Compute billed? | Meaning |
|---|---|---|
| Running | Yes | Normal operation |
| Stopped | Yes | OS shut down but hardware still reserved |
| Stopped (deallocated) | No | Hardware released; disks still billed |
Reaching a VM when SSH fails
- Run command executes a script inside the VM through the Azure agent:
az vm run-command invoke -g rg-devops-lab -n vm-web --command-id RunShellScript --scripts "systemctl status nginx". - Boot diagnostics shows the console screenshot and boot log, and the serial console gives text access even with broken networking.
- Reset password or SSH key in the portal uses the VMAccess extension.
Signing in with Entra ID
Linux VMs can accept Entra ID accounts instead of local keys with the AADSSHLoginForLinux extension; users then connect with az ssh vm -g rg-devops-lab -n vm-web, and access is controlled by the Virtual Machine User Login or Administrator Login roles.
New VMs use Trusted Launch by default, adding Secure Boot and a virtual TPM to protect against boot-level malware.