π From Zero to Secure: Deploying a Hardened Azure Environment with Terraform & Azure CLI
Tags: #azure
#devops
#cloudsecurity
#terraform
π Why This Guide?
Passing a cloud security quiz is great β but real-world deployments require more than memorizing the right answer.
This article takes the concepts behind common Azure security questions and turns them into battle-tested deployments using Azure CLI and Terraform.
Youβll learn how to:
- Build a secure Network Security Group with least privilege inbound rules.
- Detect & respond to impossible travel sign-ins.
- Manage Key Vault data-plane access with Azure RBAC.
- Map and implement Defense in Depth layers.
1οΈβ£ NSG Inbound β The Right Way
Scenario:
You need to allow HTTPS traffic from the Internet to your app subnet β but safely.
Principles:
- Restrict by port and protocol.
- Avoid
*
in source IPs unless unavoidable. - Use higher-level protections like WAF where possible.
Azure CLI:
RG=rg-secure-demo LOC=westeurope VNET=vnet-secure SUBNET=app-subnet NSG=nsg-app RULE=Allow-HTTPS-Internet PRIORITY=100 # Create RG and VNet az group create -n $RG -l $LOC az network vnet create -g $RG -n $VNET -l $LOC \ --address-prefixes 10.10.0.0/16 \ --subnet-name $SUBNET --subnet-prefix 10.10.1.0/24 # Create NSG az network nsg create -g $RG -n $NSG # Add inbound HTTPS rule az network nsg rule create \ -g $RG --nsg-name $NSG -n $RULE \ --priority $PRIORITY \ --direction Inbound --access Allow --protocol Tcp \ --source-address-prefixes Internet \ --destination-port-ranges 443 # Attach NSG to subnet az network vnet subnet update \ -g $RG --vnet-name $VNET -n $SUBNET \ --network-security-group $NSG
2οΈβ£ Detecting Impossible Travel
Concept: Impossible travel is when a user logs in from two locations so far apart that traveling between them in the elapsed time is physically impossible.
KQL Query in Log Analytics:
SigninLogs | where ResultType == 0 | project TimeGenerated, UserPrincipalName, Location = tostring(LocationDetails.countryOrRegion) | order by UserPrincipalName, TimeGenerated | extend PrevLocation = prev(Location), PrevTime = prev(TimeGenerated), PrevUser = prev(UserPrincipalName) | where UserPrincipalName == PrevUser and Location != PrevLocation | where datetime_diff('minute', PrevTime, TimeGenerated) < 60
Real-World Action:
- Enable Azure AD Identity Protection.
- Create Conditional Access policy:
- Sign-in risk = Medium+
- Action = Require MFA or Block
- Start in report-only mode, then enforce.
3οΈβ£ Key Vault Access with RBAC
Scenario:
Grant a specific Azure AD group permissions to create & delete keys in Key Vault.
Azure CLI:
RG=rg-secure-demo LOC=westeurope KV=kv-secure-$RANDOM GROUP_NAME="kv-crypto-admins" # Create Key Vault az keyvault create -n $KV -g $RG -l $LOC # Create AAD group GROUP_ID=$(az ad group create --display-name "$GROUP_NAME" --mail-nickname "$GROUP_NAME" --query id -o tsv) # Assign Key Vault Administrator role ROLE="Key Vault Administrator" SCOPE=$(az keyvault show -n $KV -g $RG --query id -o tsv) az role assignment create \ --assignee-object-id $GROUP_ID \ --assignee-principal-type Group \ --role "$ROLE" \ --scope "$SCOPE"
Best Practice:
Use RBAC instead of legacy access policies for unified permissions management.
4οΈβ£ Implementing Defense in Depth
Layer Mapping:
Layer | Controls | Azure Services |
---|---|---|
Perimeter | DDoS/WAF, TLS termination | Azure DDoS, Front Door |
Network | Segmentation, ACLs | VNet, NSG, ASG |
Compute | Hardening, patching | Azure VM, Defender for Cloud |
Identity | AuthN/Z, least privilege | Entra ID, Conditional Access |
Application | Input validation, data access | Key Vault, Managed Identity |
Data | Encryption, backups | SSE, Azure Backup |
Monitoring | Detect/respond | Log Analytics, Sentinel |
π§Ή Clean-Up
az group delete -n rg-secure-demo --yes --no-wait
π Key Takeaways
- NSG rules should be precise β no blanket
*
inbound. - Impossible travel is a high-confidence detection signal.
- Key Vault RBAC is modern, scalable, and auditable.
- Security works best in layers.
π¬ Question for you:
Whatβs one Azure security trick you use that isnβt in Microsoftβs documentation? Drop it in the comments, and Iβll build a full code example for it.
Top comments (0)