Skip to content

Jamonygr/Azure-Security-Lab

Repository files navigation

Azure Security Lab

Terraform Azure GitHub Actions License

Azure Security Lab banner

Azure Security Lab architecture

Build a practical Azure security lab with Terraform. The lab creates a small hub-spoke environment where an untrusted test client reaches a protected workload only through controlled network paths. Azure Firewall Basic sits in the hub, NSGs and route tables enforce segmentation, Key Vault stores generated secrets, Log Analytics collects diagnostics, and Azure Policy audits posture.

The design follows the same style as the Azure Landing Zone Lab: clear feature toggles, reusable modules, diagrams, wiki pages, CI checks, and hands-on validation steps.

Cost note: Azure Firewall is the main cost driver. In West Europe, Azure Firewall Basic is roughly 0.40 USD/hour plus data processing, so a full month can be about 292 USD before other resources. Deploy it for practice, validate the scenario, then destroy the lab.

Wiki-Style Knowledge Map

Use this repository like a compact security-lab encyclopedia: the root README is the landing article, and the wiki/ folder is the linked body of knowledge.

Topic Start here Deep dive
Architecture Architecture Architecture overview, addressing and routing, security controls
Technology stack What Gets Deployed Technology stack, module map, variables
Deployment lifecycle Quick Start Deploy the lab, apply-test-destroy runbook, final validation checklist
Network segmentation Lab Scenarios Firewall east-west routing, private access with Bastion
Secrets and identity Security Features Key Vault secrets, Entra test users
Detection and evidence Testing the Lab Security test matrix, Log Analytics detection, SOC training platform, KQL examples, default validation report, SOC validation report
Governance and cost Cost Estimation Policy posture, cost control, GitHub Actions
Terminology References Glossary, Microsoft Learn and product references

Master Control Panel

The main toggles live in terraform.tfvars.example and environments/lab.tfvars.

deploy_firewall                = true
firewall_sku_tier              = "Basic"
force_spoke_egress_to_firewall = false

enable_test_client_public_ip = true
deploy_bastion               = false

deploy_entra_test_users   = false
deploy_conditional_access = false
deploy_sentinel           = false
deploy_soc_content        = false
deploy_endpoint_telemetry = false
deploy_identity_domain    = false
deploy_sentinel_playbooks = false
deploy_defender_for_cloud = false
deploy_sentinel_defender_connectors = false

deploy_keyvault      = true
deploy_log_analytics = true
deploy_policy        = true
deploy_budget        = true

The default keeps the lab easy to access with a restricted public RDP rule on the test client. Set admin_source_ip_cidr to your own public IP with /32.

Table of Contents

Overview

This Terraform project creates a focused security lab, not a full enterprise landing zone. It is built to make security controls visible and testable without deploying a large stack.

Core Components

  • Hub VNet: central connectivity and Azure Firewall subnet.
  • Test spoke: untrusted Windows test client with optional restricted public RDP.
  • Protected spoke: Windows IIS web VM without public IP.
  • Azure Firewall Basic: east-west routing, web allow rules, explicit admin-protocol deny example.
  • NSGs: subnet-level allow and deny rules.
  • Route tables: force test-to-protected and protected-to-test traffic through the firewall.
  • Key Vault: generated Windows admin password and lab details.
  • Log Analytics: firewall and Key Vault diagnostics.
  • Azure Policy: audit-mode posture controls.
  • Budget: subscription budget alerts through Owner contact role.

Optional Components

Component Toggle Default Notes
Azure Bastion deploy_bastion false Easier private access, but adds hourly cost.
Entra test users/groups deploy_entra_test_users false Requires directory permissions and entra_domain_name.
Conditional Access deploy_conditional_access false Report-only MFA policy, requires Entra ID P1/P2.
Microsoft Sentinel deploy_sentinel false Useful for SOC labs, but ingestion can increase cost.
SOC content deploy_soc_content false Sentinel detections, watchlists, workbook, and hunting queries.
Endpoint telemetry deploy_endpoint_telemetry false Azure Monitor Agent for Windows events, Defender operational logs, and performance counters.
AD DS identity lab deploy_identity_domain false Adds a domain controller and domain-joins lab VMs.
Sentinel playbooks deploy_sentinel_playbooks false Adds automation rules and a Logic Apps incident playbook.
Defender for Cloud deploy_defender_for_cloud false Requires licensing and can add cost.
Defender connectors deploy_sentinel_defender_connectors false Requires Defender XDR/Sentinel tenant permissions.
Spoke default egress via firewall force_spoke_egress_to_firewall false Keep false when using public RDP to avoid asymmetric routing.

Architecture

Architecture overview

Internet
   |
   | restricted RDP from admin_source_ip_cidr
   v
Test Spoke 10.23.0.0/16
   |
   | UDR: protected spoke via firewall
   v
Hub VNet 10.20.0.0/16
   |
   | Azure Firewall Basic
   v
Protected Spoke 10.22.0.0/16
   |
   | private web VM, no public IP
   v
Protected workload

Address Plan

Zone CIDR Purpose
Hub 10.20.0.0/16 Firewall and optional Bastion
Azure Firewall subnet 10.20.1.0/24 Required reserved subnet name
Azure Firewall management subnet 10.20.3.0/24 Required by Azure Firewall Basic
Azure Bastion subnet 10.20.2.0/26 Created only when Bastion is enabled
Protected spoke 10.22.0.0/16 Private workload
Protected web subnet 10.22.1.0/24 Windows IIS web VM
Test spoke 10.23.0.0/16 Untrusted client
Test client subnet 10.23.1.0/24 Windows test VM

Lab Scenarios

Lab scenarios

The wiki now includes a Microsoft Learn-style path with objectives, diagrams, exact validation commands, expected results, troubleshooting, cleanup, and product reference links. Start with the wiki index, then use the final validation checklist before sharing or pushing the lab. For a pre-commit proof pass, use the final-check runbook and the security test matrix.

Scenario 1: Firewall in the Middle

Objective: prove that private spoke-to-spoke traffic crosses Azure Firewall.

Expected result:

  • curl http://PROTECTED_PRIVATE_IP succeeds from the test client.
  • RDP or WinRM from test to protected fails.
  • Firewall logs show allowed web flows and denied admin attempts.

Scenario 2: Network Segmentation

Objective: understand how NSGs and UDRs work together.

Controls:

  • Protected VM has no public IP.
  • Protected NSG only allows HTTP/S from the test spoke.
  • Route tables send private spoke traffic through the firewall when deploy_firewall = true.

Scenario 3: Key Vault Secrets

Objective: store generated lab access material safely.

Secrets include:

  • windows-admin-password
  • test-client-private-ip
  • protected-web-private-ip
  • protected-web-test-url
  • firewall IPs when deployed
  • Entra test user passwords when enabled

Scenario 4: Entra Test Identity

Objective: create disposable users and groups for identity/security practice.

Users:

  • sec-admin
  • sec-analyst
  • lab-user
  • break-glass

Groups:

  • security-admins
  • security-analysts
  • lab-users
  • break-glass

This scenario is disabled by default because it needs directory permissions and a verified domain.

Scenario 5: Policy Posture

Objective: practice audit-mode governance without breaking the lab.

Policies audit:

  • required environment tag
  • allowed locations
  • public IP creation
  • storage accounts without HTTPS-only traffic

Scenario 6: Detection Basics

Objective: inspect security telemetry in Log Analytics.

Included starter query:

AzureDiagnostics
| where ResourceProvider == "MICROSOFT.NETWORK"
| where Category in ("AzureFirewallNetworkRule", "AZFWNetworkRule") or Category contains "Firewall"
| where msg_s has "Deny"
| take 50

Scenario 7: SOC Training Platform

Objective: practice a full blue-team workflow with Sentinel incidents, endpoint telemetry, identity events, workbooks, watchlists, automation, and benign attack simulation.

Use the SOC profile:

terraform plan -var-file="environments/soc.tfvars"
terraform apply -var-file="environments/soc.tfvars"

Then follow the SOC training platform, SOC attack simulation, and SOC incident response runbooks.

What Gets Deployed

Default Resources

Area Resources
Resource groups hub, test, protected, security
Networking 3 VNets, subnets, peerings, NSGs, route tables
Security Azure Firewall Basic, firewall policy, rule collections
Compute 1 test Windows VM, 1 protected Windows IIS web VM
Secrets Key Vault, generated Windows admin password, lab output secrets
Monitoring Log Analytics workspace, firewall diagnostics, Key Vault diagnostics
Governance custom audit policies and assignments
Cost subscription budget alert

Deployment Profiles

Profile Key toggles Approximate cost behavior
Minimal private test deploy_firewall=false Low cost, no firewall scenario
Default security lab deploy_firewall=true, deploy_bastion=false Firewall is the main cost
Private access lab deploy_bastion=true, enable_test_client_public_ip=false More secure access, more cost
Identity lab deploy_entra_test_users=true Requires Graph permissions
SOC lab deploy_sentinel=true Higher detection value, possible ingestion cost
SOC training platform -var-file="environments/soc.tfvars" Adds Sentinel content, endpoint telemetry, AD DS, playbooks, and simulations

Quick Start

Prerequisites

  • Terraform >= 1.9.0
  • Azure CLI
  • An Azure subscription
  • Contributor rights for the subscription or target scope
  • Optional: Entra permissions if creating test users or Conditional Access

1. Configure Azure

az login
az account set --subscription "<subscription-id>"

2. Configure the lab

Copy the example file:

Copy-Item terraform.tfvars.example terraform.tfvars

Set your public IP:

admin_source_ip_cidr = "YOUR_PUBLIC_IP/32"

3. Deploy

terraform init
terraform plan -var-file="terraform.tfvars"
terraform apply -var-file="terraform.tfvars"

4. Get access details

terraform output test_client
terraform output protected_web
terraform output -raw windows_admin_password

The Windows admin password is sensitive. You can also retrieve it from Key Vault when deploy_keyvault = true.

5. Destroy when done

terraform destroy -var-file="terraform.tfvars"

Validated Environment Runs

The default lab and SOC profile have both been applied, tested, and destroyed from this workspace against the active Azure subscription.

SOC Profile: July 7, 2026

Check Result
Toolchain Terraform 1.12.0, Azure CLI 2.83.0
Static checks terraform fmt -check -recursive passed; terraform validate -no-color passed
Profile environments/soc.tfvars
Apply Sentinel, SOC content, AMA endpoint telemetry, AD DS, playbooks, and simulations deployed after fixes
Runtime test Domain controller ready; test and web VMs joined corp.securitylab.local; benign simulations ran
Telemetry AzureDiagnostics, WindowsEvent, and Heartbeat records appeared in Log Analytics
Sentinel Analytics rules, watchlists, workbook, automation rules, and Logic App playbook existed; firewall and Key Vault incidents were generated
Drift check Final SOC plan returned detailed exit code 0 before destroy
Destroy 126 destroyed; all four lab resource groups returned false; Terraform state was empty
Caveats W3CIISLog ingestion was not validated because the provider rejected the IIS file-log DCR source; Terratest must be run from tests/

See the full SOC environment test report for exact commands, findings, incidents, and fixes.

Default Profile: June 7, 2026

Check Result
Toolchain Terraform 1.12.0, Azure CLI 2.83.0
Static checks terraform fmt -check -recursive passed; terraform validate passed
Plan Initial Windows plan: 69 to add, 0 to change, 0 to destroy; recovery plan after the hostname fix: 4 to add, 0 to change, 0 to destroy
Apply Shared lab resources were created first; recovery apply created the two Windows VMs and Custom Script Extensions
Runtime test Protected IIS active; test client HTTP to protected private IP succeeded; TCP/3389 to protected VM returned ADMIN_PORT_BLOCKED
Telemetry Log Analytics showed firewall Allow for test-to-protected-web and Deny for deny-test-to-protected-admin
Destroy 69 destroyed; all four default resource groups returned false; generated local state and plan files were removed

See the full environment test report for exact commands, findings, and the Log Analytics REST workaround used for this Azure CLI install.

GitHub Actions

The repo includes .github/workflows/terraform-checks.yml for non-destructive static checks:

  • Terraform formatting
  • Terraform backend-free init and validation
  • TFLint
  • Checkov
  • Gitleaks

The workflow runs on pushes to main, pull requests, and manual dispatch. It does not apply or destroy Azure resources.

No Azure credentials are required for the default checks. If you later add a plan workflow, use GitHub Actions OIDC with Azure and remote Terraform state.

Variable Purpose
ARM_CLIENT_ID App registration or managed identity client ID for optional Azure OIDC workflows
ARM_TENANT_ID Entra tenant ID for optional Azure OIDC workflows
ARM_SUBSCRIPTION_ID Azure subscription ID for optional Azure OIDC workflows
DESTROY_CONFIRM Recommended explicit guard if a future manual destroy workflow is added

The current backend is local for easy workstation use. Before shared CI plan/apply/destroy usage, replace backend.tf with an Azure Storage backend so pipeline state is durable.

Configuration Options

Important variables:

Variable Default Description
project security Naming prefix
environment lab Environment label
location westeurope Azure region
deploy_firewall true Deploy Azure Firewall
firewall_sku_tier Basic Firewall SKU
admin_source_ip_cidr 0.0.0.0/32 Allowed RDP source
enable_test_client_public_ip true Public RDP path to test client
deploy_bastion false Optional private access
deploy_entra_test_users false Optional Entra users/groups
deploy_conditional_access false Optional report-only MFA policy
deploy_sentinel false Optional Sentinel onboarding
deploy_soc_content false Optional Sentinel SOC content
deploy_endpoint_telemetry false Optional AMA Windows event, Defender operational, and performance telemetry
deploy_identity_domain false Optional AD DS identity lab
deploy_sentinel_playbooks false Optional Sentinel automation and Logic App
deploy_defender_for_cloud false Optional Defender for Cloud server plan
deploy_sentinel_defender_connectors false Optional Defender connectors for Sentinel
deploy_keyvault true Store generated lab secrets
deploy_log_analytics true Enable central logs
deploy_policy true Enable audit policies
deploy_budget true Enable budget alert

Testing the Lab

Traffic flow

Static Checks

terraform fmt -check -recursive
terraform init -backend=false -input=false
terraform validate

Before plan, apply, or destroy, initialize the configured local backend:

terraform init -input=false

Optional tools:

tflint --init
tflint --recursive
checkov -d . --framework terraform
gitleaks detect --source . --redact

Terraform Plan

terraform plan -var-file="environments/lab.tfvars"

For the full SOC profile:

terraform plan -var-file="environments/soc.tfvars"

Review that:

  • Azure Firewall appears only when deploy_firewall = true.
  • Protected VM has no public IP.
  • Test and protected route tables point private spoke traffic at the firewall private IP.
  • Entra users are skipped when deploy_entra_test_users = false.
  • SOC plans include Sentinel content, AMA telemetry, AD DS, and playbooks only when the SOC toggles are enabled.

Manual Connectivity

From the test client:

Invoke-WebRequest -UseBasicParsing "http://PROTECTED_PRIVATE_IP"
Test-NetConnection PROTECTED_PRIVATE_IP -Port 3389

Expected:

  • HTTP succeeds.
  • RDP to protected fails.

Terratest

Integration tests live in tests/.

cd tests
go test ./...

Tests skip automatically when ARM_SUBSCRIPTION_ID is not set.

SOC Evidence

After applying environments/soc.tfvars, use:

terraform output identity_domain
terraform output soc_content
terraform output endpoint_telemetry

Then follow the SOC attack simulation and SOC incident response runbooks. Validated SOC telemetry includes AzureDiagnostics, WindowsEvent, and Heartbeat; IIS W3CIISLog detection content is included, but file-log ingestion still needs a supported collection path.

Security Features

  • Private protected workload with no public IP.
  • Restricted public RDP to test client via admin_source_ip_cidr.
  • NSG deny rules for administrative protocols.
  • Firewall policy allow and deny examples.
  • Key Vault secret storage for generated Windows admin password and lab values.
  • Log Analytics diagnostics for firewall and Key Vault.
  • Optional SOC profile with AMA endpoint telemetry, AD DS identity events, Sentinel detections, workbooks, watchlists, hunting queries, and playbooks.
  • Optional Defender for Cloud and Defender XDR connector paths.
  • Audit-mode Azure Policy assignments.
  • Optional Entra test identity and report-only Conditional Access.
  • Optional Sentinel onboarding.
  • GitHub Actions validation, linting, secret scanning, and security scanning.

Cost Estimation

Costs vary by region and usage. The largest default cost is Azure Firewall Basic.

Resource Default Cost behavior
Azure Firewall Basic On Roughly 0.40 USD/hour in West Europe plus data
2 small Windows VMs On Low, but still billable while running
Log Analytics On Controlled by log_analytics_daily_quota_gb
Key Vault On Usually low for lab usage
Bastion Off Adds hourly cost when enabled
Sentinel Off Can add ingestion/analytics cost
SOC identity domain Off Adds one Windows Server domain controller when soc.tfvars is used
Defender for Cloud Off Can add plan and ingestion costs when licensed toggles are enabled

Cost tips:

  • Destroy the lab after practice.
  • Keep deploy_bastion = false unless you need private access.
  • Keep deploy_sentinel = false until you are ready for detection scenarios.
  • Use environments/soc.tfvars for short SOC training windows, then destroy it.
  • Use the budget alert as an early warning, not as a hard cost limit.
  • Consider deploy_firewall = false when working only on docs, policy, or identity modules.

Troubleshooting

Cannot RDP to test client

  • Confirm enable_test_client_public_ip = true.
  • Confirm admin_source_ip_cidr is your current public IP with /32.
  • Confirm the test client public IP from terraform output test_client.

Public RDP breaks after forcing egress

If force_spoke_egress_to_firewall = true, public RDP return traffic can become asymmetric. Use Bastion/private access, or keep the default false.

Protected web is not reachable

  • Confirm the protected VM private IP from terraform output protected_web.
  • Confirm route tables exist when deploy_firewall = true.
  • Confirm firewall rules allow TCP 80/443 from 10.23.0.0/16 to 10.22.0.0/16.
  • Confirm Custom Script Extension completed on the protected VM.

Entra user creation fails

  • Set deploy_entra_test_users = false if you do not need identity scenarios.
  • Set entra_domain_name to a verified tenant domain.
  • Confirm the Terraform identity has permission to create users and groups.

Conditional Access fails

Conditional Access requires Entra ID P1/P2 licensing and the right Graph permissions. It is disabled by default.

Budget creation fails

Budget APIs can require subscription-level permissions. Set deploy_budget = false if your account cannot create budgets.

SOC domain or telemetry fails

Check the troubleshooting runbook for the domain readiness marker, firewall domain-service rules, AMA WindowsEvent queries, Sentinel playbook permissions, and Defender licensing notes.

Project Structure

.
|-- .github/workflows/terraform-checks.yml
|-- README.md
|-- backend.tf
|-- environments/
|-- docs/images/
|-- modules/
|   |-- bastion/
|   |-- budget/
|   |-- conditional-access/
|   |-- defender-for-cloud/
|   |-- entra-test-identity/
|   |-- firewall/
|   |-- keyvault/
|   |-- windows-vm/
|   |-- monitoring/
|   |-- networking/
|   |-- policy/
|   `-- sentinel/
|-- policies/
|-- tests/
|-- wiki/
|-- main.tf
|-- variables.tf
|-- outputs.tf
`-- terraform.tfvars.example

References

License

MIT. See LICENSE.

About

Learn Azure security with this hands-on Terraform lab. Deploys a compact hub-spoke environment with Azure Firewall Basic, NSGs, route tables, Key Vault, Log Analytics, Azure Policy, segmentation tests, detection evidence, and CI validation.

Resources

License

Stars

23 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors