Secure Azure setup with Entra ID, Bastion, and private VM


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 → ConnectBastion → 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.

error: Content is protected !!