Scope
Stand up a fresh Azure landing zone with a minimal but secure baseline: Entra ID (Azure AD) hardening, management structure, logging, networking, a Windows/Linux VM without public exposure, and safe access (Bastion + Entra sign-in).
Placeholders to replace:TENANT_NAME · MG_ROOT · SUB_NAME · RG_CORE · RG_NET · RG_VM · LOCATION · VNET_NAME · SUBNET_APP · BASTION_SUBNET · VM_NAME · VM_SIZE · ADMIN_GROUP_OBJECTID
0) Prereqs
- Azure tenant & subscription created (via portal/Commerce).
- Azure CLI logged in:
az login az account set --subscription "SUB_NAME" - Optional SKUs: Entra ID P1/P2 for Conditional Access, PIM, Identity Protection.
1) Entra ID (Tenant) Baseline
- Create two break-glass cloud-only Global Admin accounts; long passwords; exclude from CA; store offline.
- Turn on Security Defaultsor implement baseline Conditional Access:
- Require MFA for admins.
- Disable legacy/basic auth.
- Require MFA for all users or at least privileged roles.
- Enable SSPR, passwordless Authenticator (and FIDO2 keys if available).
- Use PIM for role activation (P2).
- Create AAD groups for RBAC (e.g.,
Azure-VM-Admins).
(Portal-driven; no commands included to keep this redacted.)
2) Management Structure & Tags
- Create management group root and place the subscription under it.
- Standardize tags (Owner, CostCenter, Env, DataClass).
az account management-group create -n MG_ROOT
az account management-group subscription add --name MG_ROOT --subscription "SUB_NAME"
3) Core Resource Groups & Logging
az group create -n RG_CORE -l LOCATION
az group create -n RG_NET -l LOCATION
az group create -n RG_VM -l LOCATION
# Log Analytics workspace
az monitor log-analytics workspace create -g RG_CORE -n LAW-CORE -l LOCATION
LAW_ID=$(az monitor log-analytics workspace show -g RG_CORE -n LAW-CORE --query id -o tsv)
# Send Activity Logs to LAW
az monitor diagnostic-settings create \
--name "activity-to-law" \
--resource "/subscriptions/$(az account show --query id -o tsv)" \
--workspace $LAW_ID \
--logs '[{"categoryGroup":"allLogs","enabled":true}]'
4) Guardrails with Azure Policy (minimal starter)
# Require tags
az policy assignment create -g RG_CORE -n require-tags \
--policy "Require a tag and its value on resources" \
--params '{"tagName":{"value":"Owner"},"tagValue":{"value":"REDACTED"}}'
# Allowed locations
az policy assignment create -g RG_CORE -n allowed-locations \
--policy "Allowed locations" \
--params '{"listOfAllowedLocations":{"value":["LOCATION"]}}'
Enable Microsoft Defender for Cloud and auto-provision agents (portal) to get JIT VM access recommendations and secure score.
5) Networking (no public RDP/SSH)
# VNet + subnets
az network vnet create -g RG_NET -n VNET_NAME -l LOCATION \
--address-prefixes 10.10.0.0/16 \
--subnet-name SUBNET_APP --subnet-prefix 10.10.10.0/24
# Dedicated Bastion subnet (must be exactly AzureBastionSubnet)
az network vnet subnet create -g RG_NET --vnet-name VNET_NAME \
-n AzureBastionSubnet --address-prefixes 10.10.254.0/27
# NSG and rules (deny inbound by default; allow vnet)
az network nsg create -g RG_NET -n NSG-APP
az network nsg rule create -g RG_NET --nsg-name NSG-APP -n Allow-VNet \
--priority 100 --access Allow --direction Inbound --protocol '*' \
--source-address-prefixes VirtualNetwork --source-port-ranges '*' \
--destination-address-prefixes VirtualNetwork --destination-port-ranges '*'
# Associate NSG to the app subnet
az network vnet subnet update -g RG_NET --vnet-name VNET_NAME -n SUBNET_APP \
--network-security-group NSG-APP
6) Bastion (safe console access)
# Public IP for Bastion
az network public-ip create -g RG_NET -n pip-bastion -l LOCATION --sku Standard --zone 1 2 3
# Bastion host
az network bastion create -g RG_NET -n bas-VNET_NAME -l LOCATION \
--public-ip-address pip-bastion --vnet-name VNET_NAME
7) VM (managed identity, no public IP, Entra login)
Windows example:
# NIC (no public IP)
az network nic create -g RG_VM -n nic-VM_NAME \
--vnet-name VNET_NAME --subnet SUBNET_APP
# VM
az vm create -g RG_VM -n VM_NAME \
--image Win2022Datacenter --size VM_SIZE \
--nics nic-VM_NAME --assign-identity \
--admin-username "localadmin" --admin-password "GENERATE-STRONG-PASSWORD" \
--enable-agent true --os-disk-size-gb 128
# Enable AAD login extension (Windows)
az vm extension set -g RG_VM -n AADLoginForWindows --publisher Microsoft.Azure.ActiveDirectory \
--vm-name VM_NAME
# Grant Entra groups the VM login roles
VM_ID=$(az vm show -g RG_VM -n VM_NAME --query id -o tsv)
az role assignment create --assignee-object-id ADMIN_GROUP_OBJECTID \
--role "Virtual Machine Administrator Login" --scope $VM_ID
Linux example (SSH keys + AAD login):
az vm create -g RG_VM -n VM_NAME \
--image Ubuntu2204 --size VM_SIZE \
--nics nic-VM_NAME --assign-identity \
--authentication-type ssh --ssh-key-values ~/.ssh/id_rsa.pub
# Enable AAD SSH login (Linux)
az vm extension set -g RG_VM -n AADSSHLoginForLinux --publisher Microsoft.Azure.ActiveDirectory \
--vm-name VM_NAME
# RBAC for login
az role assignment create --assignee-object-id ADMIN_GROUP_OBJECTID \
--role "Virtual Machine Administrator Login" --scope $VM_ID
Accessing the VM (no public IP):
- Portal → Resource → Connect → Bastion → Open session (RDP for Windows, SSH for Linux).
- Optionally enable Just-In-Time in Defender for Cloud; keep NSG closed otherwise.
8) Backup, Patching, and Keys
# Recovery Services vault + VM backup
az backup vault create -g RG_CORE -n rsv-core -l LOCATION
az backup protection enable-for-vm -g RG_CORE -v rsv-core --vm VM_NAME --policy-name "DefaultPolicy"
# VM guest patching (Update Manager) – enable in portal for the RG/VM
- Store secrets/keys in Azure Key Vault; use managed identity from the VM to fetch secrets.
- Use Server-side encryption (SSE) with platform-managed keys (default) or customer-managed keys (CMK) via Key Vault if required.
9) Monitoring (Guest + Platform)
# Enable VM Insights / Diagnostics to LAW
az monitor diagnostic-settings create \
--name "vm-to-law" \
--resource $VM_ID --workspace $LAW_ID \
--metrics '[{"category":"AllMetrics","enabled":true}]' \
--logs '[{"categoryGroup":"allLogs","enabled":true}]'
10) Cost Guardrails
- Create a Budget in Cost Management with email alerts at 50/80/100%.
- Consider Reservations and Auto-shutdown on dev/test VMs.
11) Access Patterns to Prefer
- Bastion or Private endpoints; avoid public RDP/SSH.
- Entra sign-in to VMs with RBAC (
Virtual Machine User/Administrator Login). - PIM + MFA for privileged roles.
- JIT for any temporary inbound need.
Minimal Tear-down (lab)
# Danger: deletes resources
az group delete -n RG_VM -y
az group delete -n RG_NET -y
az group delete -n RG_CORE -y
Notes & Deviations
- For domain-join scenarios, use Entra ID DS (managed domain) or a full AD DS in Azure; keep DCs on a separate subnet with restricted NSG.
- For Intune/MDM of servers, consider Azure Arc + Defender for Endpoint.
- Replace all placeholders and remove screenshots/IDs before publishing externally.
For more info:
Microsoft Entra ID overview/service description. Microsoft Learn
• Connect to a VM using Azure Bastion (private IP). Microsoft Learn
• Private Endpoint / Private Link overview & quickstart. Microsoft Learn+1
© 2012–2025 Jet Mariano. All rights reserved.
For usage terms, please see the Legal Disclaimer.