A virtual network (VNet) is a private address space inside one region, divided into subnets. A network security group (NSG) filters traffic with priority-ordered allow and deny rules, and can be attached to a subnet or to a single network interface. A virtual machine sits in a subnet through a network interface. Those three objects are enough to run a first server safely, and they map closely to AWS concepts: VNet to VPC, NSG to security group plus network ACL, VM to EC2 instance.
What we are building
flowchart TB
INTERNET[Internet] -->|HTTPS 443| LB[Public IP or load balancer optional]
ADMIN[Admin on corporate IP] -->|SSH 22 only from my IP, or Bastion| VM
subgraph RG[Resource group demo-rg - Central India]
subgraph VNET[VNet demo-vnet 10.30.0.0/16]
subgraph SUBA[Subnet app 10.30.1.0/24]
NSG[NSG demo-nsg<br/>allow 22 from my IP, allow 443] --- NIC[Network interface]
NIC --- VM[VM Ubuntu 22.04 Standard_B2s<br/>managed disk Standard SSD]
end
SUBB[Subnet AzureBastionSubnet 10.30.254.0/26<br/>optional Bastion]
end
PIP[Public IP Standard SKU static]
end
PIP --- NIC
VM -->|outbound internet| INTERNET
DISK[(OS disk - billed while it exists)] --- VM
Create it with the CLI
The portal is good for learning, but commands are repeatable and show every object that gets created.
az login
az account set --subscription "shop-dev"
RG=demo-rg; LOC=centralindia
az group create --name $RG --location $LOC --tags Env=dev Owner=raviteja
az network vnet create --resource-group $RG --name demo-vnet --address-prefix 10.30.0.0/16 \
--subnet-name app --subnet-prefix 10.30.1.0/24
MYIP=$(curl -s https://api.ipify.org)
az network nsg create --resource-group $RG --name demo-nsg
az network nsg rule create --resource-group $RG --nsg-name demo-nsg --name allow-ssh-admin \
--priority 100 --direction Inbound --access Allow --protocol Tcp \
--source-address-prefixes $MYIP/32 --destination-port-ranges 22
az network nsg rule create --resource-group $RG --nsg-name demo-nsg --name allow-https \
--priority 110 --direction Inbound --access Allow --protocol Tcp \
--source-address-prefixes Internet --destination-port-ranges 443
az network vnet subnet update --resource-group $RG --vnet-name demo-vnet --name app \
--network-security-group demo-nsg
az vm create --resource-group $RG --name demo-vm \
--image Canonical:0001-com-ubuntu-server-jammy:22_04-lts-gen2:latest \
--size Standard_B2s --admin-username azureuser --generate-ssh-keys \
--vnet-name demo-vnet --subnet app --nsg "" \
--public-ip-sku Standard --os-disk-size-gb 30 --storage-sku StandardSSD_LRS
az vm show --resource-group $RG --name demo-vm --show-details --query publicIps -o tsv
Then connect:
ssh azureuser@<public-ip>
Notes on the choices:
| Setting | Why |
|---|---|
Image Ubuntu Server 22.04 LTS (Gen 2) |
Current long-term support release; pick Gen 2 images for modern VM sizes |
Standard_B2s |
Burstable, cheap, fine for light work; sustained load needs a non-burstable size such as Standard_D2s_v5 |
| SSH key, no password | Password logins on a public IP are attacked within minutes |
--nsg "" |
The subnet-level NSG we attached applies; avoids a second NIC-level NSG |
| Standard public IP | Zone-capable, secure by default (closed unless an NSG allows traffic); the older Basic SKU is being phased out, so avoid it for new work |
| Standard SSD | Good balance for general workloads; Premium SSD for databases and heavy I/O |
How NSG rules are evaluated
Rules are processed in priority order (lowest number first) and processing stops at the first match. Every NSG also has default rules at priority 65000 and above: allow traffic inside the VNet, allow the Azure load balancer’s probes, deny all other inbound; outbound allows the VNet and Internet by default. Create your own rules at priorities 100 to 4096.
Tips: use service tags (Internet, VirtualNetwork, Storage) and application security groups to describe sources instead of lists of IPs; keep rules few; and enable NSG flow logs (via Network Watcher) when you need to investigate traffic. Azure reserves five addresses in every subnet (the first four and the last), so a /29 gives only three usable addresses.
Notes for AWS users
- Outbound internet access is allowed by default for VMs in a VNet through Azure-provided default outbound access, so there is no separate NAT step for a simple case. For production use an explicit method (NAT Gateway or a load balancer’s outbound rules) so source IPs are predictable.
- Public IPs, disks and NICs are separate resources. Stopping a VM from inside the OS (
sudo shutdown) leaves it allocated and still billing compute. Use Stop (deallocate) in the portal oraz vm deallocateto end compute charges; the OS disk, static public IP and snapshots keep billing. - Deleting a VM does not always delete its disk, NIC and public IP; delete the resource group to remove an experiment completely.
- Subnets are regional; the VNet spans the region, and availability zones are chosen per VM or per zone-redundant service, not per subnet.
- The built-in admin account is
azureuser(you choose it), notubuntu.
Do not leave SSH open
- Use Azure Bastion: browser or CLI-based RDP and SSH over TLS to VMs that have no public IP. It needs a subnet named exactly
AzureBastionSubnet(at least /26) and costs an hourly rate, so it suits shared environments rather than a throwaway test. - Or just-in-time VM access in Microsoft Defender for Cloud, which opens the port for your IP only for a short window.
- Or a VPN / ExpressRoute from the office, and no public IP on VMs at all.
- Restrict the NSG rule to specific source IPs, as above, and remove the rule when finished.
Also enable boot diagnostics, Azure Monitor agent metrics and automatic OS patching (Update Manager) for anything that is not a throwaway.
Tidy up and control cost
az vm deallocate --resource-group $RG --name demo-vm # stop paying for compute
az vm start --resource-group $RG --name demo-vm
az group delete --name $RG --yes --no-wait # remove everything in the experiment
az consumption usage list --top 5 -o table # quick cost glimpse (cost analysis in the portal is richer)
Set a budget with alert thresholds under Cost Management before creating anything else, and use an auto-shutdown schedule for development VMs (VM, Auto-shutdown). Tag every resource so you can find its owner later.
Troubleshooting connectivity
| Symptom | Check |
|---|---|
| SSH times out | NSG rule source IP (your address may have changed), public IP attached, VM running |
Permission denied (publickey) |
Wrong username or key; confirm with az vm user update or reset the SSH key |
| VM cannot reach the internet | Outbound NSG rule, route table / firewall, or no outbound method on a private subnet |
| Cannot reach another VM | Different VNet without peering, or NSG/ASG rule on the destination |
| Port open but no response | Service not listening or host firewall (ufw); test with ss -tlnp on the VM |
Use Network Watcher (IP flow verify, next hop, connection troubleshoot) to see which rule or route is responsible.