Spinning up your first Azure Virtual Machine is the moment cloud stops being abstract. You click a few times, wait ninety seconds, and you are holding the keyboard of a real Windows or Linux server in a Microsoft datacentre — one you can SSH or RDP into from your laptop, install anything on, and tear down for nothing when you are done. An Azure VM is Infrastructure as a Service (IaaS): Azure runs the physical host, the hypervisor, the network fabric and the storage; you own everything from the operating system up. That single line — “OS and up is yours” — explains every decision you are about to make, from which image to pick to which firewall ports to open.
The trouble is that the create-a-VM experience asks roughly thirty questions across five tabs, and a beginner has no idea which ones matter. Pick the wrong VM size and you overpay or get a sluggish box; leave the wrong port open and you have published RDP to the whole internet within the hour; forget auto-shutdown and a forgotten test VM bills you for a month. This guide walks every one of those decisions, then deploys the same VM three ways so you understand what the clicks actually do: first in the Azure portal (to see every field), then with the az CLI (to script it), then as a Bicep template (to version-control and recreate it identically).
By the end you will have a running, reachable VM you built deliberately — not one you clicked “Create” on and hoped for. You will know what each resource it spawned is for, how to connect securely, how to read the cost, and — the part most tutorials skip — how to delete every trace of it so your free credit lasts. We use a Linux (Ubuntu) VM with SSH as the primary example because it is cheapest and clearest, and call out the Windows + RDP differences at every step.
What problem this solves
You need a server — to host a website, run a background job, try a database, practise Linux, or reproduce a production issue. On-premises that means procuring hardware, racking it, installing an OS, waiting weeks. With an Azure VM it is a few minutes, billed by the second, thrown away when you are finished. A VM is the most familiar unit of compute there is: a whole machine you control — the right first step before containers or serverless.
What goes wrong without a deliberate process: beginners accept every default — an oversized SKU billing 24×7, port 3389 (RDP) or 22 (SSH) open to 0.0.0.0/0, a public IP they did not know costs money, and no idea which of the six spawned resources to keep. Within a week that is a security incident (bots find open RDP in minutes) or a bill that eats their ₹15,000 free credit on one idle box. The fix is simply knowing what each field means and choosing on purpose. This bites everyone new to Azure, every AZ-900/AZ-104 candidate, and every PaaS-only developer needing a raw box — and it is foundational, since AKS nodes and App Service workers are VMs underneath.
The resources one VM actually creates, so nothing in the wizard surprises you:
| Resource the wizard creates | What it is | Costs money? | Can you reuse it? | Notes |
|---|---|---|---|---|
| Virtual machine | The compute instance (CPU + RAM) | Yes (per second, when running) | n/a | Deallocate to stop compute billing |
| OS disk (managed disk) | The boot disk holding the OS | Yes (always, even when VM is stopped) | Yes (can reattach) | Premium SSD vs Standard SSD/HDD |
| Network interface (NIC) | Connects the VM to a subnet | No (the NIC itself) | Yes | One VM can have several |
| Public IP address | Internet-routable address | Yes (Standard SKU, hourly) | Yes (if static) | Skip it for private-only VMs |
| Network security group (NSG) | The VM/subnet firewall | No | Yes | Where you control SSH/RDP access |
| Virtual network + subnet | The private network it lives in | No | Yes (and you should) | Created once, reused by many VMs |
Learning objectives
By the end of this article you can:
- Explain what an Azure VM is, what Azure manages versus what you manage, and the six resources a single VM deployment creates.
- Choose a sensible region, VM size (SKU), image, disk type and authentication method for a first VM, and justify each choice.
- Deploy an Ubuntu Linux VM end-to-end three ways — Azure portal,
azCLI, and Bicep — with the correct NSG rules for safe SSH (and the RDP equivalents for Windows). - Connect to the VM securely over SSH (Linux) or RDP (Windows), and validate at each step that the previous step worked.
- Restrict management ports to your own IP so you never expose SSH/RDP to the public internet.
- Read what a VM costs, set auto-shutdown, deallocate versus stop, and tell the difference on the bill.
- Tear the whole thing down cleanly by deleting the resource group, leaving zero billable leftovers.
Prerequisites & where this fits
You need an Azure subscription (a free account gives ~₹15,000 / US$200 credit for 30 days) and either the Azure portal or Azure Cloud Shell / a local az CLI. No prior VM experience is assumed; you should be comfortable pasting terminal commands. The Linux path needs an SSH client (built into macOS, Linux, modern Windows), the Windows path an RDP client (built into Windows; Microsoft Remote Desktop on macOS).
This sits at the base of the Compute track. Upstream: every VM goes into a resource group (Azure Resource Hierarchy Explained: Subscriptions, Resource Groups and Resources) and onto a virtual network (Azure Virtual Network, Subnets and NSGs: Networking Fundamentals); which datacentre — and whether you spread across zones — is Azure Regions and Availability Zones: Designing for Resilience. Downstream, once it runs, you protect it (Protect Your First Azure VM with Azure Backup: A Guided Walkthrough) and watch it (Azure Monitor and Application Insights: Full-Stack Observability).
The division of responsibility — the most important IaaS mental model, and a frequent exam question:
| Layer | Who manages it on a VM | Why it matters to you |
|---|---|---|
| Physical datacentre, host hardware, hypervisor | Microsoft | You never patch firmware or replace disks |
| Physical network, host-level DDoS, fabric | Microsoft | The plumbing is handled |
| Guest OS (Ubuntu/Windows) and patches | You | You run apt upgrade / Windows Update |
| Runtime, middleware, apps | You | Your stack, your responsibility |
| OS firewall + NSG rules | You | You decide who can reach SSH/RDP |
| Data on the disks | You | You back it up and encrypt it |
| Identity & access (who can manage the VM) | You (via Azure RBAC) | You grant least privilege |
Core concepts
A handful of terms recur through every step; pin them down and the wizard reads like plain English.
A VM is rented by size (SKU), billed by the second, and stoppable. The VM size (Standard_B1s, Standard_D2s_v5, …) fixes vCPUs, RAM and price. You pay compute only while running (allocated). Deallocating (the portal “Stop”) releases the hardware and ends that charge — but the disk and any static public IP keep billing. Shutting the OS down from inside the guest does not deallocate; Azure still bills compute. This surprises everyone once.
The OS comes from an image. An image is a disk template — Ubuntu 22.04 LTS, Windows Server 2022, Red Hat, thousands more in the Azure Marketplace — that becomes the VM’s OS disk. Base images carry no software charge (infrastructure only); some commercial images add a per-hour fee.
Disks are managed and come in tiers. Managed disks are virtual disks Azure provisions for you; the tier sets performance and price — Standard HDD (cheapest), Standard SSD (dev/test), Premium SSD (production default), Ultra/Premium SSD v2 (demanding databases). A first VM uses Standard SSD; production app servers want Premium SSD.
The VM lives on a network guarded by an NSG. It gets a NIC on a subnet inside a virtual network (VNet) — a private IP space that is yours. A Network Security Group (NSG) is a stateful allow/deny firewall (port, protocol, source, destination) on the NIC or subnet, where you permit SSH (22) or RDP (3389) — and, critically, from where. Golden rule: never allow management ports from Any; scope them to your own IP.
Reaching it from the internet needs a public IP. By default a VM has only a private IP. To connect from your laptop you attach a public IP (simple, but exposing) or use a safer path — Azure Bastion or a VPN. This lab uses a public IP locked to your source IP; production prefers Bastion.
With those five models in hand, the next section makes each concrete choice — and tells you exactly what to pick for a first VM.
Choosing the right options before you click create
These five choices account for almost all of a beginner’s regret. Make them deliberately and the rest is mechanical.
Region — where the VM physically runs
A region is a datacentre cluster (Central India, East US, …). Pick the one closest to you or your users for lowest latency; prices differ by region (Central India/East US beat Japan East). Resilience later means Availability Zones; a first VM needs no zone. Not every size exists everywhere — if a SKU is greyed out, switch region or size.
VM size — burstable B-series is the right first pick
For learning, dev/test and low-traffic workloads the B-series (burstable) is purpose-built and cheapest: a low baseline CPU that banks credits while idle, bursting when busy. Reach for D-series only for sustained CPU. The practical first-VM size menu:
| Size | vCPU | RAM | Family | Good for | Rough cost (running 24×7) |
|---|---|---|---|---|---|
Standard_B1s |
1 | 1 GiB | Burstable | Smallest learning box, tiny services | ~₹650–900 / ~US$8–11 per month |
Standard_B2s |
2 | 4 GiB | Burstable | Comfortable dev box, small web app | ~₹2,600–3,200 / ~US$31–38 |
Standard_B2ms |
2 | 8 GiB | Burstable | More memory for dev/test | ~₹4,000–4,800 / ~US$48–58 |
Standard_D2s_v5 |
2 | 8 GiB | General purpose | Steady CPU, small production | ~₹6,500–7,500 / ~US$78–90 |
Indicative pay-as-you-go Linux prices that vary by region and currency; confirm in the Azure Pricing Calculator. Start at B1s/B2s, step up to D-series for sustained CPU, and turn the VM off when not in use.
Image — Linux is cheaper and clearer; Windows when you need it
Pick Ubuntu 22.04 LTS for the cheapest, fastest-booting, license-free box. Pick Windows Server 2022 when you need IIS, the .NET Framework, AD tooling or Windows-only software; Windows adds a license cost to the per-hour price (or use Azure Hybrid Benefit). The image also fixes login: Linux → SSH, Windows → RDP.
Authentication — SSH keys over passwords, always
For Linux choose SSH public key: the public key goes on the VM, the private key stays on your laptop — no password to brute-force (bots hammer passwords on internet-facing Linux within minutes). For Windows you must use a username + strong password (RDP) — long, unique, port still locked to your IP. Compared:
| Method | OS | Security | Convenience | Use when |
|---|---|---|---|---|
| SSH public key | Linux | High (no password to guess) | Generate key once | Always, for Linux |
| Password | Linux | Low (brute-forceable) | No key to manage | Avoid; only throwaway labs |
| Username + password | Windows | Medium (strength-dependent) | Familiar | Windows VMs (required for RDP) |
| Microsoft Entra login | Linux & Windows | High (central identity, MFA) | Extra setup | Org-managed fleets later |
Inbound ports — open nothing to the world
The wizard offers to open SSH/RDP/HTTP, defaulting the source to Any. Do not accept that. Open only the management port you need (22 or 3389), then scope its source to your own public IP. The single most common first-VM mistake is leaving 3389/22 open to 0.0.0.0/0. The small set of ports you will actually touch:
| Port | Protocol | Purpose | Open it from | Never open from |
|---|---|---|---|---|
| 22 | TCP (SSH) | Linux remote shell | Your IP only | Any / 0.0.0.0/0 |
| 3389 | TCP (RDP) | Windows remote desktop | Your IP only | Any / 0.0.0.0/0 |
| 80 | TCP (HTTP) | Web server (if you host one) | Any is OK for a public site |
— |
| 443 | TCP (HTTPS) | Secure web (if you host one) | Any is OK for a public site |
— |
Architecture at a glance
Hold the shape as three layers, left to right. On the left, you at your laptop with a private SSH key and a known public IP. In the middle, the Azure edge: your request hits a public IP bound to the VM’s network interface (NIC), guarded by a network security group whose one inbound rule says “TCP 22 from my IP” — everything else denied. On the right, the VM inside a subnet (10.0.0.0/24) of a virtual network (10.0.0.0/16), with a private IP, an OS disk (Standard SSD holding Ubuntu), and its compute.
When you type ssh azureuser@<public-ip>, the packet flows laptop → internet → public IP → NSG (checked, allowed) → NIC → VM, where sshd answers on 22; because the NSG is stateful, that one inbound rule suffices and the reply returns automatically. That is the whole model — a guarded door (NSG) in front of a networked box (NIC + subnet) backed by a disk, reached through one address — all in a single resource group so cleanup is one command. The lab builds exactly this; the teardown deletes exactly this.
Real-world scenario
Meghana, a backend developer at a 40-person Pune logistics startup, RouteCrate, is told on a Friday to “stand up a box to run the new label-printing service over the weekend so QA can hammer it Monday.” It needs Python and a few packages, must be reachable by three QA engineers, and must not cost much — the team’s Azure budget is tracked tightly in INR.
She has never built a VM solo. Her first instinct is the portal wizard, and she nearly clicks straight through: it defaulted to a Standard_D2s_v5 with Premium SSD and offered SSH from Any. She catches herself — switches to Standard_B2s (plenty for a label service at a fraction of the price), the disk to Standard SSD, picks Ubuntu 22.04 LTS and SSH public key, and lets it create the VNet and NSG, planning to fix the SSH source after. The VM is up in a minute; she immediately changes the NSG SSH source from Any to her office CIDR 203.0.113.0/24 plus the three engineers’ home IPs, SSHes in (chmod 600 on the key first — a step the portal never reminds you about), runs sudo apt update && sudo apt install -y python3-pip, deploys, and opens port 8080 only to the same trusted IPs.
The Friday discipline pays off twice. When one QA engineer “can’t connect,” she checks the NSG, sees their IP missing (mobile data, not office WiFi), and adds it in thirty seconds — far better than the box having been open to the world. And auto-shutdown at 9 PM IST means it runs ~30 hours over the weekend instead of 72, costing under ₹300. On Monday she captures it as Bicep — the next environment is one az deployment command. The lesson she repeats to new hires: the wizard’s defaults optimise for “it works,” not “cheap and safe” — those two you choose yourself.
Advantages and disadvantages
A VM gives you a whole machine, which is its strength and its burden. The trade-off, plainly:
| Advantages of a VM | Disadvantages of a VM |
|---|---|
| Full control — any OS, any software, kernel-level access | You own all OS patching, hardening and maintenance |
| Familiar mental model (it’s just a server) | More to manage than PaaS/serverless (no auto-scale by default) |
| Runs literally anything, including legacy apps | Pay even when idle unless you deallocate |
| Lift-and-shift from on-prem with minimal change | You must secure it (open ports = your problem) |
| Predictable, fixed performance per SKU | Slower to scale than containers/functions |
| Per-second billing; throw it away anytime | Disk bills continue while the VM is merely stopped |
Right tool: legacy or stateful apps, a specific OS/kernel, GPU workloads, full-control dev boxes, lift-and-shift. Wrong tool: stateless web apps (App Service), event-driven glue (Functions), microservices at scale (AKS/Container Apps) — all of which hand OS-patching and scaling back to Azure. As a first step, though, the VM teaches the fundamentals everything else builds on.
Hands-on lab
This is the centrepiece. You will deploy the same Ubuntu VM three ways — portal, az CLI, Bicep — connect, validate, and tear it down. The portal shows every field, the CLI shows how fast scripting is, the Bicep shows version-controlled infra. Everything goes into one resource group, rg-firstvm-lab. Windows + RDP differences are called out throughout.
Cost note: a
Standard_B1swith a Standard SSD, run only during the lab and then deleted, costs a few rupees. Do not leave it running — the teardown removes everything.
Part 0 — Prerequisites and a key pair
-
Have an Azure subscription (a free account works) and sign in to
portal.azure.com. -
Pick your tooling: for the CLI parts, Azure Cloud Shell (the
>_icon) hasazpre-installed and pre-authenticated; or install the CLI locally and runaz login. -
Know your own public IP (you will lock SSH/RDP to it):
curl -s https://api.ipify.org ; echo # → e.g. 203.0.113.45 (this is YOUR_IP for the rest of the lab)Expected: one IPv4 address; use it as
YOUR_IP/32. -
Generate an SSH key pair (skip if you use the portal’s “Generate new key pair”):
ssh-keygen -t rsa -b 4096 -f ~/.ssh/firstvm_key -N "" # Creates ~/.ssh/firstvm_key (private) and ~/.ssh/firstvm_key.pub (public)Expected: two files; the
.pubone is the public key you hand to Azure.
Part A — Deploy via the Azure portal (see every field)
For understanding, fill the five tabs deliberately.
- Click Create a resource → Virtual machine (or search “Virtual machines” → Create). You land on the Basics tab.
- Project details:
- Subscription: your subscription.
- Resource group: Create new, name it
rg-firstvm-lab— your blast radius and cleanup target.
- Instance details:
- Virtual machine name:
vm-firstvm-01. - Region: pick the closest cheap region, e.g. (Asia Pacific) Central India or (US) East US.
- Availability options: No infrastructure redundancy required.
- Security type: Standard (or Trusted launch).
- Image: Ubuntu Server 22.04 LTS - x64 Gen2. (Windows: Windows Server 2022 Datacenter.)
- Size: See all sizes → Standard_B1s (B2s for more headroom).
- Virtual machine name:
- Administrator account:
- Authentication type: SSH public key (Windows: Password — set a strong username/password); Username:
azureuser. - SSH public key source: Use existing public key and paste
~/.ssh/firstvm_key.pub, or Generate new key pair (download the private key now — you cannot re-download it).
- Authentication type: SSH public key (Windows: Password — set a strong username/password); Username:
- Inbound port rules: Allow selected ports → SSH (22) (Windows: RDP (3389)). Heed the “exposed to the internet” warning — we tighten the source in step 9.
- Next: Disks → OS disk type: Standard SSD (LRS); leave the rest default. (Premium SSD for production.)
- Next: Networking → let Azure create new for the Virtual network (
10.0.0.0/16), Subnet (10.0.0.0/24), Public IP (Standard), and NSG = Basic with the SSH rule. Leave Delete public IP and NIC when VM is deleted ticked. - Skip Management/Monitoring/Advanced/Tags, click Review + create. Expected output: a green Validation passed banner and a cost-per-hour estimate.
- Click Create. Deployment runs ~30–90 s. Expected output: Your deployment is complete, listing the VM, disk, NIC, public IP, NSG and VNet. Click Go to resource.
- Lock the SSH source to your IP — immediately. VM blade → Networking → the port-22 inbound rule → change Source from Any to IP Addresses → Source IP addresses/CIDR ranges =
YOUR_IP/32→ Save. Expected: the rule shows your IP; the internet can no longer reach port 22.
Validate: on the VM Overview, copy the Public IP address, then from your laptop:
chmod 600 ~/.ssh/firstvm_key # macOS/Linux: private key must not be world-readable
ssh -i ~/.ssh/firstvm_key azureuser@<PUBLIC_IP>
# Accept the host fingerprint the first time (type: yes)
Expected: an Ubuntu shell prompt like azureuser@vm-firstvm-01:~$. You are in. (Windows: open Remote Desktop, enter the public IP, then azureuser and your password.)
Part B — Deploy via the az CLI (the fast, scriptable way)
The same VM in a handful of commands, in Cloud Shell or a local authenticated az. The VM name (vm-firstvm-cli) differs from Part A’s, so both share the rg-firstvm-lab group.
-
Set variables (edit
YOUR_IP):RG=rg-firstvm-lab LOC=centralindia # or eastus VM=vm-firstvm-cli YOUR_IP=203.0.113.45 # from Part 0, step 3 -
Create the resource group:
az group create --name $RG --location $LOCExpected: JSON with
"provisioningState": "Succeeded". -
Create the VM — one command builds the VNet, subnet, NIC, public IP, NSG (SSH open) and disk:
az vm create \ --resource-group $RG \ --name $VM \ --image Ubuntu2204 \ --size Standard_B1s \ --admin-username azureuser \ --ssh-key-values ~/.ssh/firstvm_key.pub \ --storage-sku StandardSSD_LRS \ --public-ip-sku Standard \ --nsg-rule SSH \ --output tableExpected: a table with
publicIpAddress,privateIpAddress,powerState: VM running. Note the publicIpAddress.To auto-generate a key instead, replace
--ssh-key-values ...with--generate-ssh-keys. (Windows: drop the SSH flags, add--admin-password '<StrongP@ssw0rd!>', use--image Win2022Datacenterand--nsg-rule RDP.) -
Lock SSH to your IP (
--nsg-rule SSHopened it to Any). Find the NSG name, then update the rule source:NSG=$(az network nsg list -g $RG --query "[0].name" -o tsv) az network nsg rule update \ --resource-group $RG --nsg-name $NSG --name default-allow-ssh \ --source-address-prefixes $YOUR_IP/32 \ --output tableExpected: the rule with
sourceAddressPrefix: 203.0.113.45/32; the world can no longer reach port 22.If the auto-created rule has a different name, list with
az network nsg rule list -g $RG --nsg-name $NSG -o tableand update the SSH one. -
Connect and validate:
IP=$(az vm show -d -g $RG -n $VM --query publicIps -o tsv) ssh -i ~/.ssh/firstvm_key azureuser@$IPExpected: the Ubuntu prompt. (Inside,
nproc && free -h && lsb_release -areports 1 vCPU, ~1 GiB, Ubuntu 22.04 — see Part D.) -
Optional — set auto-shutdown so you never forget it running:
az vm auto-shutdown \ --resource-group $RG --name $VM \ --time 1530 --email h.vinod@gmail.com # 1530 UTC = 21:00 ISTExpected: JSON confirming the
shutdown-computevm-...schedule.
The CLI flags, so you can adapt the command:
| Flag | What it sets | Common values | If you omit it |
|---|---|---|---|
--image |
OS image | Ubuntu2204, Win2022Datacenter, RHELRaw9 |
Required (no default) |
--size |
VM SKU | Standard_B1s, Standard_B2s, Standard_D2s_v5 |
Defaults to a D-series (often pricier) |
--admin-username |
Login user | azureuser |
Required |
--ssh-key-values / --generate-ssh-keys |
SSH public key | path to .pub, or auto-generate |
Prompts/uses password |
--storage-sku |
OS disk tier | StandardSSD_LRS, Premium_LRS |
Defaults to Premium (costs more) |
--nsg-rule |
Auto inbound rule | SSH, RDP, NONE |
Opens nothing if NONE |
--public-ip-sku / --zone |
Public IP tier / availability zone | Standard; 1/2/3 |
Standard default; no zone |
Part C — Deploy via Bicep (version-controlled, repeatable)
Bicep is Azure’s native infrastructure-as-code language. The self-contained file below builds the same VM — VNet, public IP, NSG, NIC and VM — and is what you commit to git so any teammate recreates it identically. Save it as firstvm.bicep.
param adminUsername string = 'azureuser'
param sshPublicKey string // contents of ~/.ssh/firstvm_key.pub
param mySourceIp string // e.g. '203.0.113.45/32' — locks SSH to you
param location string = resourceGroup().location
param vmName string = 'vm-firstvm-bicep'
resource vnet 'Microsoft.Network/virtualNetworks@2023-09-01' = {
name: '${vmName}-vnet'
location: location
properties: {
addressSpace: { addressPrefixes: ['10.0.0.0/16'] }
subnets: [ { name: 'default', properties: { addressPrefix: '10.0.0.0/24' } } ]
}
}
resource pip 'Microsoft.Network/publicIPAddresses@2023-09-01' = {
name: '${vmName}-pip'
location: location
sku: { name: 'Standard' }
properties: { publicIPAllocationMethod: 'Static' }
}
resource nsg 'Microsoft.Network/networkSecurityGroups@2023-09-01' = {
name: '${vmName}-nsg'
location: location
properties: {
securityRules: [ {
name: 'Allow-SSH-From-MyIP'
properties: {
priority: 1000
direction: 'Inbound'
access: 'Allow'
protocol: 'Tcp'
sourceAddressPrefix: mySourceIp // your IP only — never '*'
sourcePortRange: '*'
destinationAddressPrefix: '*'
destinationPortRange: '22'
}
} ]
}
}
resource nic 'Microsoft.Network/networkInterfaces@2023-09-01' = {
name: '${vmName}-nic'
location: location
properties: {
ipConfigurations: [ {
name: 'ipconfig1'
properties: {
subnet: { id: '${vnet.id}/subnets/default' } // the subnet created above
publicIPAddress: { id: pip.id }
privateIPAllocationMethod: 'Dynamic'
}
} ]
networkSecurityGroup: { id: nsg.id }
}
}
resource vm 'Microsoft.Compute/virtualMachines@2023-09-01' = {
name: vmName
location: location
properties: {
hardwareProfile: { vmSize: 'Standard_B1s' }
storageProfile: {
imageReference: { publisher: 'Canonical', offer: '0001-com-ubuntu-server-jammy', sku: '22_04-lts-gen2', version: 'latest' }
osDisk: { createOption: 'FromImage', managedDisk: { storageAccountType: 'StandardSSD_LRS' } }
}
osProfile: {
computerName: vmName
adminUsername: adminUsername
linuxConfiguration: {
disablePasswordAuthentication: true
ssh: {
publicKeys: [ {
path: '/home/${adminUsername}/.ssh/authorized_keys'
keyData: sshPublicKey
} ]
}
}
}
networkProfile: { networkInterfaces: [ { id: nic.id } ] }
}
}
output publicIp string = pip.properties.ipAddress
Deploy it into the same resource group:
az deployment group create \
--resource-group rg-firstvm-lab \
--template-file firstvm.bicep \
--parameters \
sshPublicKey="$(cat ~/.ssh/firstvm_key.pub)" \
mySourceIp="203.0.113.45/32"
Expected: "provisioningState": "Succeeded" and an outputs.publicIp value; SSH to it as before. The win over both prior methods: this NSG is born locked to your IP — no “open then tighten” gap, because sourceAddressPrefix is your IP from the first deploy. That is why teams move VM creation into Bicep.
Part D — Validate the deployment
Whichever path you used, confirm the box is healthy:
| Check | Command / action | Expected result |
|---|---|---|
| VM is running | az vm get-instance-view -g $RG -n $VM --query "instanceView.statuses[?starts_with(code,'PowerState')].displayStatus" -o tsv |
VM running |
| You can SSH in | ssh -i ~/.ssh/firstvm_key azureuser@$IP |
Ubuntu shell prompt |
| Port 22 is locked to you | az network nsg rule list -g $RG --nsg-name $NSG --query "[?destinationPortRange=='22'].sourceAddressPrefix" -o tsv |
your IP/32, not * |
| Size & OS match | inside the VM: nproc; free -h; lsb_release -d |
1 vCPU, ~1 GiB, Ubuntu 22.04 |
| Outbound works | inside the VM: `curl -sI https://www.microsoft.com | head -1` |
Part E — Teardown (do not skip this)
One command deletes the VM, disk, NIC, public IP, NSG and VNet together — zero billable leftovers:
az group delete --name rg-firstvm-lab --yes --no-wait
az group exists --name rg-firstvm-lab # → false once deletion finishes
Expected: the prompt returns immediately (--no-wait); deletion finishes in a couple of minutes. (In the portal: Resource groups → rg-firstvm-lab → Delete.) This is why everything went into one group: delete the group, and the bill stops. A VM “stopped” from inside the OS, or a disk orphaned by a half-deleted VM, keeps charging — the group delete guarantees a clean exit.
Common mistakes & troubleshooting
The failures every first-timer hits, with the exact way to confirm and fix each.
| # | Symptom | Root cause | How to confirm | Fix |
|---|---|---|---|---|
| 1 | ssh hangs then times out |
NSG source is not your IP (or your IP changed) | az network nsg rule list -g $RG --nsg-name $NSG -o table — check the port-22 source |
Update the rule source to your current IP/32 |
| 2 | Permission denied (publickey) |
Wrong key, or wrong username | ssh -v -i <key> azureuser@IP shows which key is offered |
Use the matching private key; user is azureuser |
| 3 | WARNING: UNPROTECTED PRIVATE KEY FILE! |
Private key is world-readable (macOS/Linux) | ls -l ~/.ssh/firstvm_key shows rw-r--r-- |
chmod 600 ~/.ssh/firstvm_key |
| 4 | RDP “can’t connect” to Windows VM | Port 3389 not open to your IP, or VM still booting | NSG rule for 3389; VM Overview shows running | Open 3389 from your IP; wait for boot to finish |
| 5 | az vm create fails: SKU not available |
That size isn’t offered in that region | az vm list-skus -l $LOC --size Standard_B --query "[].name" -o tsv |
Pick an available size or change region |
| 6 | Quota error: “Operation could not be completed… quota” | Subscription vCPU quota for that family is 0/low (common on new/free) | error names the family + region | Pick a family you have quota for, or request a quota increase |
| 7 | Bill is higher than expected on an idle VM | You stopped the OS but didn’t deallocate | Portal shows Stopped not Stopped (deallocated) | az vm deallocate -g $RG -n $VM to release compute |
| 8 | VM deleted but charges continue | Orphaned managed disk / static public IP left behind | az disk list -g $RG -o table; az network public-ip list -g $RG -o table |
Delete the resource group, not just the VM |
| 9 | Forgot the password / lost the SSH key | No way to log in | n/a | Reset via VM → Reset password (uses the VMAccess extension) |
| 10 | Can SSH but apt/outbound fails |
An NSG/UDR blocks outbound, or there is no outbound path | inside VM curl -I https://microsoft.com hangs |
Ensure default outbound is allowed; see the VNet guide below |
For connectivity problems that survive the table — packets that leave your laptop but never reach the VM, or outbound that mysteriously fails — work through Diagnosing Azure VNet Connectivity: NSGs, UDRs, Effective Routes & Network Watcher, whose effective security rules and Network Watcher tools show exactly which rule allowed or dropped a flow.
The single highest-value habit: when SSH/RDP won’t connect, check the NSG source first — the cause of most first-VM connection failures, almost always because the rule is still Any or your home IP rotated.
Best practices
- Lock management ports to your IP, never
Any. Open 22/3389 only from yourIP/32; better, use private IPs behind Azure Bastion or a VPN. - Use SSH keys, not passwords for Linux; long unique passwords for Windows RDP; Microsoft Entra login for managed fleets.
- Right-size, then turn it off. Start at
B1s/B2s, set auto-shutdown, and deallocate (not just stop) when idle. - Put everything in a dedicated resource group so teardown is one command and the blast radius is contained.
- Standard SSD for dev/test, Premium SSD for production — match the disk tier to the workload.
- Capture the VM as Bicep once it works, so the next environment is reproducible and secure by default.
- Tag resources (
owner,env,costCenter) so cost and ownership stay clear. - Patch the guest OS — IaaS means you own updates; use
apt upgrade/ Windows Update or Azure Update Manager. - Back up anything you care about before it becomes production.
- Delete the resource group when a lab is done — a forgotten VM, disk or static IP is the classic surprise bill.
Security notes
A VM exposed to the internet is attacked continuously, so security is non-negotiable from minute one. Least exposure: allow management ports only from trusted sources — an open 0.0.0.0/0 rule on 22 or 3389 is found and brute-forced within minutes. Beyond a throwaway lab, give the VM no public IP: use a private subnet behind Azure Bastion (browser-based RDP/SSH, no public IP, no open ports) or a VPN.
Identity: control who manages the VM with Azure RBAC — grant Virtual Machine Contributor, not Owner; for logging into the OS, Microsoft Entra login brings MFA/Conditional Access to SSH/RDP. Secrets: never bake credentials into the VM — store them in Azure Key Vault: Secrets, Keys and Certificates Done Right and give the VM a managed identity to fetch them. Encryption: managed disks are encrypted at rest by default; enable Azure Disk Encryption or customer-managed keys for stricter needs. Patching: an unpatched guest OS is the most common real VM compromise — keep it current, store the SSH key with chmod 600, never commit it to git, and rotate it if exposed.
Cost & sizing
A VM’s bill has three independent parts. Compute is charged per second while allocated (running) and is the largest line — it stops the instant you deallocate. Disk is charged continuously, even when stopped, until you delete it. Public IP (Standard) carries a small hourly charge whether or not the VM runs. What drives the bill:
| Cost driver | Billed when | Rough order of magnitude | How to control it |
|---|---|---|---|
| Compute (the VM size) | While running/allocated | ₹650+ / ~US$8+ per month for B1s, up linearly with SKU | Deallocate when idle; auto-shutdown; smaller SKU |
| OS managed disk | Always (even stopped) | ~₹150–500 / ~US$2–6 per month (Standard vs Premium) | Standard SSD for dev; delete with the RG |
| Public IP (Standard) | While allocated to a resource | ~₹250–350 / ~US$3–4 per month | Skip it (use Bastion); release when unused |
| Outbound data transfer | Per GB egressed to internet | Pennies for light use; free inbound | Keep traffic in-region; cache |
| Software (some images) | Per hour for commercial images | Varies; Ubuntu/Win base = infra only | Use license-free images; Hybrid Benefit |
Two facts save the most money: deallocate ≠ stop-from-inside (only the portal Stop / az vm deallocate ends the compute charge), and a deleted VM can keep billing if its disk or static public IP are left behind — hence tearing down the whole resource group. For sizing, start small: B1s for learning, B2s/B2ms for a dev box, D-series only for sustained CPU. Free credit (~₹15,000 / US$200 over 30 days) covers far more VM-hours than a beginner uses if you turn the box off — a B1s run two hours a day for a month is under ₹100. Confirm rates in the Azure Pricing Calculator and set a budget and alert in Cost Management.
Interview & exam questions
1. Difference between “Stopped” and “Stopped (deallocated)”?
“Stopped” (a shutdown from inside the guest) leaves the VM allocated, so compute keeps billing. “Stopped (deallocated)” — the portal Stop or az vm deallocate — releases the compute; you still pay disks and any static public IP. (AZ-900 / AZ-104.)
2. In the shared responsibility model, who manages what for an IaaS VM? Microsoft manages the datacentre, hardware, hypervisor and physical network. You manage the guest OS and its patches, runtime, apps, OS/NSG firewall, data, and identity. Rule of thumb: “from the OS up is yours.” (AZ-900.)
3. Why never open RDP (3389) or SSH (22) to Any/0.0.0.0/0?
Internet-facing management ports are scanned and brute-forced within minutes of exposure. Scope the NSG source to your own IP/CIDR, or drop the public IP and use Azure Bastion. (AZ-104 / SC-900.)
4. What is a Network Security Group and where do you attach it? A stateful firewall of priority-ordered allow/deny rules (direction, protocol, port, source, destination), attached to a subnet and/or a NIC. Being stateful, allowing inbound automatically permits the return traffic. (AZ-104.)
5. What does the B-series give you and when is it right? A low baseline CPU that banks credits while idle and bursts when busy — cheap and ideal for dev/test, low-traffic servers and learning. Wrong for sustained high-CPU work, which needs D-series or compute-optimised. (AZ-104.)
6. Name the resources a single VM deployment creates. The VM, an OS managed disk, a NIC, optionally a public IP, an NSG, and a VNet/subnet. This matters for cost (disks and static IPs bill independently) and cleanup (delete the resource group). (AZ-104.)
7. What disk types does Azure offer and which fits a first VM? Standard HDD (cheapest), Standard SSD (dev/test), Premium SSD (low-latency, production default) and Ultra/Premium SSD v2 (demanding databases). A first or dev VM uses Standard SSD; production uses Premium or higher. (AZ-104.)
8. Why prefer SSH keys over passwords for Linux VMs? Key auth removes a guessable secret — no password to brute-force. The private key stays on your machine, the public key on the VM; with port 22 locked to your IP, attack surface drops sharply. (AZ-104 / SC-900.)
9. How do you connect to a VM with no public IP? Use Azure Bastion (managed browser-based RDP/SSH over the private network), or a VPN/ExpressRoute into the VNet, then reach the private IP — the recommended production pattern. (AZ-104.)
10. What is Bicep and why deploy a VM with it instead of the portal? Bicep is Azure’s declarative infrastructure-as-code language (compiles to ARM). It makes deployments repeatable, version-controlled and reviewable, and bakes in safe defaults — like an NSG rule locked to your IP — so every environment is identical and secure by construction. (AZ-104.)
11. Why might az vm create fail with a quota or “SKU not available” error?
“SKU not available” means that size is not offered in that region — change size or region. A quota error means the subscription’s vCPU quota for that family/region is exhausted (often 0 on new/free subscriptions); request an increase or pick a family with headroom. (AZ-104.)
12. After deleting a VM, why might billing continue, and how do you prevent it? The managed disk and any static public IP survive a VM delete and keep billing. Deploy into a dedicated resource group and delete the group to remove all dependent resources at once. (AZ-900 / AZ-104.)
Quick check
- You shut the VM down from inside Ubuntu with
sudo poweroff. Are you still paying for compute? Why? - Which port do you open for Linux remote access, which for Windows, and from what source should each be allowed?
- Name three resources, besides the VM itself, that a portal VM deployment creates.
- Your
sshconnection times out immediately. What is the most likely cause and the first thing to check? - What single Azure CLI command removes the VM and its disk, NIC, public IP, NSG and VNet together?
Answers
- Yes. A shutdown from inside the guest leaves the VM allocated to hardware, so compute keeps billing. Only deallocating (portal Stop or
az vm deallocate) releases the hardware and stops the compute charge. - Port 22 (SSH) for Linux, port 3389 (RDP) for Windows — each allowed only from your own IP/CIDR, never from
Any/0.0.0.0/0. - Any three of: OS managed disk, network interface (NIC), public IP address, network security group (NSG), virtual network + subnet.
- The NSG inbound rule for your management port is not allowing your source IP — it is still
Any(insecure) or your home IP changed. Check/Update the rule’s source to your currentIP/32first. az group delete --name rg-firstvm-lab --yes— deleting the resource group removes every resource inside it in one operation.
Glossary
- Azure Virtual Machine (VM): An IaaS compute instance — a virtual server where you control the OS and up.
- IaaS (Infrastructure as a Service): Provider runs the hardware/hypervisor/network; you manage the OS upward.
- VM size (SKU): The named tier (e.g.
Standard_B1s) fixing vCPU, RAM and price. - Image: A disk template (Ubuntu 22.04, Windows Server 2022, …) that becomes the VM’s OS disk.
- Managed disk: An Azure-provisioned virtual disk; tiers Standard HDD, Standard SSD, Premium SSD, Ultra/Premium SSD v2.
- Deallocate: Releasing a stopped VM’s hardware so compute billing stops (disks/static IP still bill); not an in-guest shutdown.
- Resource group: A container for resources; deleting it deletes its contents.
- Virtual network (VNet) / subnet: Your private IP space in Azure, and a segment of it the VM joins.
- Network interface (NIC): The virtual NIC connecting a VM to a subnet, carrying its private (and optional public) IP.
- Network Security Group (NSG): A stateful allow/deny firewall on a subnet or NIC; where you control inbound 22/3389.
- Public IP address: An internet-routable address on a NIC so the VM is reachable outside the VNet.
- SSH (port 22): The encrypted remote-shell protocol for Linux VMs; key auth strongly preferred.
- RDP (port 3389): The remote-desktop protocol for Windows VMs.
- Azure Bastion: Managed browser-based RDP/SSH over the private network — no public IP, no open ports.
- Bicep: Azure’s declarative infrastructure-as-code language (compiles to ARM) for repeatable, reviewable deployments.
Next steps
- Protect the box you just built: Protect Your First Azure VM with Azure Backup: A Guided Walkthrough — add a backup policy so a mistake or failure is recoverable.
- Understand the network it lives on: Azure Virtual Network, Subnets and NSGs: Networking Fundamentals — go deeper on subnets, NSG rule evaluation, and private connectivity.
- Choose where it runs for resilience: Azure Regions and Availability Zones: Designing for Resilience — spread VMs across zones once uptime matters.
- Keep secrets off the VM: Azure Key Vault: Secrets, Keys and Certificates Done Right — fetch credentials at runtime via a managed identity instead of storing them on the box.
- Watch it in production: Azure Monitor and Application Insights: Full-Stack Observability — collect metrics, logs and alerts so you see problems before users do.