Skip to main content
Azure · lesson 3 of 10
Azure beginner 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

azure virtual-machines networking ssh

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.

Frequently Asked Questions

What VM size stays in Azure's free tier?
B1S — one vCPU, 1 GiB of RAM — is covered for 750 hours a month under the free tier for the first 12 months of a new account. It's underpowered for real workloads but enough to prove out create-connect-delete workflows, which is what this lesson does.
Why does az vm create ask for or generate an SSH key instead of a password?
Linux VMs on Azure default to key-based authentication because it isn't guessable or brute-forceable the way a password is, and Azure disables SSH password authentication by default on images that support it. If you don't pass --ssh-key-values, az vm create generates a new key pair and saves the private key locally.
What is a network security group and how is it different from a firewall on the VM itself?
A network security group (NSG) is an Azure resource that filters traffic at the network level, before it reaches the virtual machine's network interface — the equivalent of a cloud-managed firewall sitting in front of the VM. It's separate from any OS-level firewall (like ufw or iptables) running inside the VM; both have to allow a port for traffic to get through.
Why did SSH still fail after I opened port 22 in the NSG?
The NSG is only one of several places traffic can be blocked. Check that the VM has a public IP assigned, that no OS-level firewall inside the VM is also blocking port 22, and that you're using the correct public IP and the private key that matches the public key registered on the VM.