Skip to content

Terraform Plan Review Guide

Chris Panagiotidis edited this page Jun 13, 2026 · 1 revision

Terraform Plan Review Guide

This page explains how to review a Terraform plan for the Azure Landing Zone Lab before apply. The goal is to catch security, reliability, cost, and drift problems before Azure resources change.

Use this page with:

Review Inputs

Collect these before approving an apply:

Input Source
Environment profile environments/<profile>.tfvars
Terraform plan summary GitHub Actions plan job
Plan artifact GitHub Actions artifact
Security scan output tfsec, Checkov, Gitleaks
OPA output policy-check job
Cost estimate Infracost job or manual Azure calculator review
Changed files Pull request diff

Safe Local Plan Pattern

For syntax and basic review without remote backend:

terraform init -backend=false
terraform plan -var-file=environments/cheap-lab.tfvars -out=tfplan
terraform show -no-color tfplan
terraform show -json tfplan > tfplan.json

Do not commit tfplan, tfplan.json, .terraform, or backend config files.

First-Pass Plan Questions

Question Expected answer
Is the selected profile correct? Yes, usually cheap-lab first
Are there unexpected destroys? No
Are high-cost resources enabled? Only if intentionally selected
Are public IPs expected? Only for approved public entry points
Is public RDP blocked? Yes
Are private endpoints and private DNS aligned? Yes
Are diagnostics wired to real resources? Yes for deployed supported resources
Are tags applied consistently? Yes

Security Review Checklist

Control Pass condition Fail example
Public RDP No broad 0.0.0.0/0 RDP source Jumpbox source allows all internet
LB RDP NAT enable_lb_rdp_nat_rules = false by default NAT rules expose workload VMs on 3389/3390
SQL public access Disabled unless explicitly justified SQL public network access enabled in lab
Key Vault network ACL Deny-by-default when private endpoint path is intended Public network default allow without reason
Storage public access Public blob access disabled Anonymous blob access enabled
Private DNS Linked to VNets that need resolution Private endpoint exists but name does not resolve
State Remote backend used with protected storage Local state committed or copied into docs
CI auth OIDC only AZURE_CREDENTIALS client secret JSON required

Cost Review Checklist

Expensive services should be intentional:

Resource type Common reason to enable Review action
Azure Firewall Central inspection scenario Confirm short-lived lab need
VPN Gateway Site-to-site or on-prem simulation Confirm SKU and destroy plan
Application Gateway WAF Public web ingress scenario Confirm WAF mode and backend health
NAT Gateway Stable outbound SNAT Confirm required subnets only
AKS Kubernetes workload scenario Confirm node count and SKU
SQL Data tier scenario Confirm public access disabled and SKU
Windows VMs AD DS and IIS lab Confirm shutdown schedule or destroy date

Pass condition: cost estimate is reviewed and high-cost toggles are either off or documented.

Networking Review Checklist

Area What to inspect
CIDR overlap No VNet address ranges overlap
Peering Hub and enabled spokes have bidirectional peering
Routes Firewall route next hop exists only when firewall is enabled
NSGs Inbound rules are scoped and tier-specific
Public ingress Application Gateway, Load Balancer, and VPN are intentional
Private endpoints Private endpoint subnet and DNS zones are present together
Flow logs VNet flow logs are enabled only when planned

Plan JSON Checks

Use JSON to confirm critical controls:

terraform show -json tfplan > tfplan.json

Review targets:

Control Search intent
Public IPs Count azurerm_public_ip resources
NSG rules Look for destination port 3389 and source 0.0.0.0/0
SQL Confirm public_network_access_enabled is false when SQL is enabled
Key Vault Confirm network ACL intent
Storage Confirm HTTPS-only and public blob access disabled
Diagnostics Confirm azurerm_monitor_diagnostic_setting resources for deployed services

Expected OPA behavior:

  • Deny unsafe defaults such as missing tags, invalid regions, or public SQL access.
  • Warn on public IPs, broad inbound NSGs, large VM SKUs, or missing hardening options.

Example Approval Notes

Use this style in a PR or run review:

Plan reviewed for cheap-lab.
Adds: expected hub, identity, management, shared, and workload resources.
Changes: none unexpected.
Destroys: 0.
Public exposure: no public RDP and no LB RDP NAT.
Cost: Firewall, VPN, App Gateway, NAT Gateway, AKS disabled.
Security gates: fmt, validate, tfsec, Checkov, Gitleaks, OPA passed.
Approved for apply.

Stop Conditions

Do not approve apply when:

  • Destroy count is non-zero and not explained.
  • Public RDP appears in plan.
  • A public data plane is enabled without a written reason.
  • Backend state settings are unknown.
  • Cost estimate includes high-cost services that were not requested.
  • Security gates are skipped, cancelled, or soft-failed unexpectedly.
  • The plan was generated from a different commit than the apply.

Next page: Azure Portal Validation Evidence

Clone this wiki locally