Azure · lesson 3 of 10
Azure Virtual Machines Fundamentals
Create a Linux VM with az vm create, open port 22 through its network security group, and connect over SSH using the generated key pair.
By SkillByExample · Updated
An Azure VM is really four resources created together: the virtual machine itself, a
network interface, a public IP, and a network security group that filters what reaches it.
az vm create creates all four in one call with sensible defaults, but knowing they’re
separate resources matters the moment something doesn’t connect.
Creating the VM
az vm create \
--resource-group learn-rg \
--name learn-vm \
--image Ubuntu2204 \
--size Standard_B1s \
--admin-username azureuser \
--generate-ssh-keys
$ az vm create --resource-group learn-rg --name learn-vm --image Ubuntu2204 --size Standard_B1s --admin-username azureuser --generate-ssh-keys
{
"fqdns": "",
"id": "/subscriptions/3b1f9c2a-6d4e-4a7b-9c11-2f5e8a0b7d33/resourceGroups/learn-rg/providers/Microsoft.Compute/virtualMachines/learn-vm",
"location": "eastus",
"macAddress": "00-0D-3A-1F-6C-2B",
"powerState": "VM running",
"privateIpAddress": "10.0.0.4",
"publicIpAddress": "20.121.34.87",
"resourceGroup": "learn-rg",
"zones": ""
}
--generate-ssh-keys writes a new key pair to ~/.ssh/id_rsa (or reuses one already there)
and registers the public half on the VM automatically — no separate key-creation step.
publicIpAddress is what SSH connects to; privateIpAddress only matters to resources
inside the same virtual network.
Trying to connect before the port is open
ssh azureuser@20.121.34.87
$ ssh azureuser@20.121.34.87
ssh: connect to host 20.121.34.87 port 22: Connection timed out
az vm create opens port 22 in the NSG by default for images it recognizes as SSH-capable —
this timeout would actually be unusual on a fresh Ubuntu VM. It’s shown here because it’s
exactly what you’d see if a stricter base image, or an NSG rule added later, blocked the
port: the connection doesn’t get refused, it hangs until it times out, which is the
signature of a firewall drop rather than nothing listening.
Confirming and, if needed, adding the NSG rule
az network nsg rule list --resource-group learn-rg --nsg-name learn-vmNSG --output table
$ az network nsg rule list --resource-group learn-rg --nsg-name learn-vmNSG --output table
Name ResourceGroup Priority SourcePortRanges SourceAddressPrefix Access Protocol Direction
--------------------------- --------------- ---------- ------------------ ---------------------- -------- ---------- -----------
default-allow-ssh learn-rg 1000 * * Allow Tcp Inbound
If that rule were missing, adding it looks like this:
az network nsg rule create \
--resource-group learn-rg \
--nsg-name learn-vmNSG \
--name allow-ssh \
--priority 1000 \
--direction Inbound \
--access Allow \
--protocol Tcp \
--destination-port-ranges 22 \
--source-address-prefixes "203.0.113.50/32"
$ az network nsg rule create --resource-group learn-rg --nsg-name learn-vmNSG --name allow-ssh --priority 1000 --direction Inbound --access Allow --protocol Tcp --destination-port-ranges 22 --source-address-prefixes "203.0.113.50/32"
{
"access": "Allow",
"destinationPortRange": "22",
"direction": "Inbound",
"name": "allow-ssh",
"priority": 1000,
"protocol": "Tcp",
"sourceAddressPrefix": "203.0.113.50/32",
"sourcePortRange": "*"
}
Scoping --source-address-prefixes to your own IP rather than * (any address) matters:
the default rule Azure creates is open to the internet on port 22, which is fine for a
throwaway lesson VM but is the first thing to lock down on anything longer-lived.
Connecting over SSH
ssh azureuser@20.121.34.87
$ ssh azureuser@20.121.34.87
The authenticity of host '20.121.34.87 (20.121.34.87)' can't be established.
ED25519 key fingerprint is SHA256:k3F8mQ2vN7xR1pL9wZ6yT4jH0cS5bA8dE2gV1nX3fY0.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '20.121.34.87' (ED25519) to the list of known hosts.
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-1058-azure x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
Last login: never
azureuser@learn-vm:~$
The host key prompt on first connect is unrelated to Azure — it’s plain OpenSSH verifying it hasn’t connected to this IP under a different identity before, the same prompt you’d see connecting to any new server.
Practice
1. Check the VM's power state without re-running the create command.
az vm get-instance-view --resource-group learn-rg --name learn-vm --query "instanceView.statuses[1].displayStatus" --output tsv
$ az vm get-instance-view --resource-group learn-rg --name learn-vm --query "instanceView.statuses[1].displayStatus" --output tsv
VM running
instanceView.statuses returns two entries — a provisioning status and a power status; index
[1] is the power state. az vm show alone reports the last-known provisioning state, not
whether the VM is actually running right now.
2. Stop the VM (deallocate it) so it stops billing for compute.
az vm deallocate --resource-group learn-rg --name learn-vm
$ az vm deallocate --resource-group learn-rg --name learn-vm
No output on success, and it takes a minute or two. az vm stop shuts down the guest OS but
Azure still reserves — and may still bill for — the compute allocation; deallocate fully
releases it. Note the public IP is usually released too unless it was created as Static.
3. Resize the VM to a larger size.
az vm resize --resource-group learn-rg --name learn-vm --size Standard_B2s
$ az vm resize --resource-group learn-rg --name learn-vm --size Standard_B2s
{
"hardwareProfile": {
"vmSize": "Standard_B2s"
}
}
Resizing a running VM restarts it — a short outage — because vCPU and memory are properties of the hypervisor allocation, not something that can change under a live guest OS.
4. List the NSG rules sorted by priority, and explain why priority matters.
az network nsg rule list --resource-group learn-rg --nsg-name learn-vmNSG --query "sort_by(@, &priority)" --output table
$ az network nsg rule list --resource-group learn-rg --nsg-name learn-vmNSG --query "sort_by(@, &priority)" --output table
Name Priority Access Direction
------------------ ---------- -------- -----------
allow-ssh 1000 Allow Inbound
default-allow-ssh 1000 Allow Inbound
Lower numbers evaluate first, and the first rule that matches a packet wins — later rules with overlapping scope are never consulted. Two rules at the same priority for overlapping traffic is a configuration smell, even if both happen to allow the same thing.
5. Delete the VM and confirm its disk and NIC are gone too, or explain why they aren't.
az vm delete --resource-group learn-rg --name learn-vm --yes
az resource list --resource-group learn-rg --output table
$ az vm delete --resource-group learn-rg --name learn-vm --yes
$ az resource list --resource-group learn-rg --output table
Name ResourceGroup Location Type
---------------- --------------- ---------- --------------------------------------------
learn-vm_OsDisk_1 learn-rg eastus Microsoft.Compute/disks
learn-vmVNET learn-rg eastus Microsoft.Network/virtualNetworks
learn-vmNSG learn-rg eastus Microsoft.Network/networkSecurityGroups
learn-vm-ip learn-rg eastus Microsoft.Network/publicIPAddresses
az vm delete removes only the compute resource — the VM object itself. The OS disk,
network interface, public IP, and NSG are separate resources that outlive it and keep
billing (the disk especially) until deleted individually or with az group delete.
Next: Blob Storage — accounts, containers, upload and download, and the access tiers that trade retrieval speed for storage cost.