Azure Networking + Terraform

Build Azure Network Security Groups with Terraform

Once you understand Azure NSGs conceptually, the next step is to build them with Terraform. This helps turn traffic filtering rules into reusable infrastructure as code that can be reviewed, versioned, and deployed consistently.

This page shows a practical Azure NSG Terraform example, explains the important blocks, and gives you a strong foundation you can extend later with subnet associations, NIC associations, route tables, and more advanced network security patterns.

Quick summary

Before going deeper, here is the simplest way to think about Azure NSG Terraform.

What Terraform code that creates an Azure Network Security Group and its security rules.
Why It makes traffic-filtering policy consistent, reviewable, and reusable across environments.
When Use it whenever you want repeatable network security rule deployment through infrastructure as code.
Where It fits into landing zones, workload security patterns, subnet protection, and CI/CD-driven infrastructure workflows.
Who Cloud, DevOps, platform, security, and infrastructure teams usually manage it.
How Using azurerm_network_security_group and usually azurerm_network_security_rule.

Why use Terraform for Azure NSGs?

NSGs can be created manually in the Azure portal, but Terraform is better when you need standardization, peer review, repeatable rule creation, and cleaner multi-environment rollout.

  • It keeps traffic-filtering rules in code instead of clicks.
  • It makes rule changes visible in Git and code review.
  • It reduces manual mistakes in priorities, naming, and port/IP matching.
  • It helps platform teams roll out consistent security policy across environments.
  • It fits well into CI/CD and reusable Terraform modules.
Simple idea: Terraform turns NSG design from a one-time portal setup into a reusable and reviewable security pattern.

Terraform code

This example creates:

  • an Azure resource group
  • one Azure Network Security Group
  • three explicit rules inside that NSG
terraform {
  required_version = ">= 1.5.0"

  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.100"
    }
  }
}

provider "azurerm" {
  features {}
}

resource "azurerm_resource_group" "network_rg" {
  name     = var.resource_group_name
  location = var.location
}

resource "azurerm_network_security_group" "web_nsg" {
  name                = var.nsg_name
  location            = azurerm_resource_group.network_rg.location
  resource_group_name = azurerm_resource_group.network_rg.name

  tags = {
    environment = var.environment
    managed_by  = "terraform"
    purpose     = "traffic-filtering"
  }
}

resource "azurerm_network_security_rule" "allow_https_inbound" {
  name                        = "Allow-HTTPS-Inbound"
  priority                    = 100
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "443"
  source_address_prefix       = "Internet"
  destination_address_prefix  = "*"
  resource_group_name         = azurerm_resource_group.network_rg.name
  network_security_group_name = azurerm_network_security_group.web_nsg.name
}

resource "azurerm_network_security_rule" "allow_app_to_db" {
  name                        = "Allow-App-To-DB"
  priority                    = 110
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "1433"
  source_address_prefix       = "10.20.2.0/24"
  destination_address_prefix  = "10.20.3.0/24"
  resource_group_name         = azurerm_resource_group.network_rg.name
  network_security_group_name = azurerm_network_security_group.web_nsg.name
}

resource "azurerm_network_security_rule" "deny_all_inbound" {
  name                        = "Deny-All-Inbound"
  priority                    = 400
  direction                   = "Inbound"
  access                      = "Deny"
  protocol                    = "*"
  source_port_range           = "*"
  destination_port_range      = "*"
  source_address_prefix       = "*"
  destination_address_prefix  = "*"
  resource_group_name         = azurerm_resource_group.network_rg.name
  network_security_group_name = azurerm_network_security_group.web_nsg.name
}

Code breakdown

Terraform block

This defines the Terraform version and AzureRM provider version. Keeping provider versions controlled helps avoid surprise changes later.

Provider block

The AzureRM provider is what Terraform uses to communicate with Azure and manage resources.

Resource group

Azure resources need a resource group, so this is usually created early in the configuration.

Network security group resource

The azurerm_network_security_group resource creates the NSG container itself. The actual traffic behavior comes from the rules attached to it.

Network security rule resources

The azurerm_network_security_rule resources define how Azure evaluates traffic by source, destination, protocol, port, direction, priority, and action.

Why separate rule resources are helpful

Many teams prefer separate rule resources because they are easier to read, review, and manage than very large inline rule blocks inside the NSG resource.

Important NSG rule fields in Terraform

A good Azure NSG Terraform page should explain that rules are not only about “open port” logic. Each rule is really a traffic-matching definition.

Field What it controls Example
direction Whether the rule applies to inbound or outbound traffic Inbound
access Whether the rule allows or denies matching traffic Allow
protocol Which protocol the rule applies to Tcp
source_address_prefix Where the traffic starts from Internet or 10.20.2.0/24
destination_address_prefix Where the traffic is going 10.20.3.0/24 or *
source_port_range The client-side source port *
destination_port_range The target service port 443, 22, 1433
priority The order Azure evaluates the rule 100
Important reminder: A correct port alone is not enough. The source, destination, protocol, and priority also need to match the intended traffic path.

Suggested variables file

Variables make NSG Terraform code more reusable across development, testing, and production environments.

variable "resource_group_name" {
  description = "Name of the Azure resource group"
  type        = string
}

variable "location" {
  description = "Azure region for deployment"
  type        = string
  default     = "South Africa North"
}

variable "nsg_name" {
  description = "Name of the Azure Network Security Group"
  type        = string
}

variable "environment" {
  description = "Environment name"
  type        = string
  default     = "dev"
}

Example terraform.tfvars

resource_group_name = "rg-network-dev-san-1"
location            = "South Africa North"
nsg_name            = "nsg-web-dev-san-1"
environment         = "dev"

Suggested outputs

Outputs help expose NSG IDs and names so they can be used by subnet association resources, NIC association resources, or downstream modules.

output "nsg_name" {
  value = azurerm_network_security_group.web_nsg.name
}

output "nsg_id" {
  value = azurerm_network_security_group.web_nsg.id
}

How to run it

A simple Terraform execution flow looks like this:

terraform init
terraform fmt
terraform validate
terraform plan
terraform apply

In real teams, these steps often run through CI/CD after review rather than manually every time.

Good habit: Always run terraform fmt and terraform validate before planning or applying changes.

Real-world usage

This NSG Terraform pattern can grow into real production security policy workflows.

Web-tier protection

A platform team allows only HTTPS from the internet to the web subnet, while denying all other direct inbound traffic.

Database protection

A team allows SQL traffic on destination port 1433 only from the application subnet source range, not from other sources.

Standardized security module

Terraform modules define repeatable NSG patterns so multiple application teams get consistent security baselines across environments.

Important mindset: Start with a few very clear rules. As the environment grows, expand into reusable patterns for subnet associations, NIC associations, and shared policy modules.

Best practices

  • Use clear rule names that reflect purpose.
  • Be deliberate with priority numbers.
  • Keep source and destination matching specific where possible.
  • Focus on destination service ports carefully.
  • Use separate Terraform rule resources for readability in larger environments.
  • Document why a rule exists, not just what it allows.
  • Avoid broad allow rules unless truly required.
  • Keep NSG design aligned with subnet purpose and traffic flow.
Strong next step: After this NSG example, a natural progression is Route Tables Terraform, subnet associations, NIC associations, and then broader Azure security architecture patterns.

Common mistakes

  • Using the correct destination port but the wrong source range.
  • Creating rules that never match because of priority order.
  • Using too many broad allow rules.
  • Hardcoding too much instead of using variables.
  • Confusing NSG creation with NSG association.
  • Ignoring whether the rule should be inbound or outbound.
Common beginner trap: Just because Terraform created the NSG successfully does not mean the rule logic is correct. Traffic design still needs to be validated carefully.

Frequently asked questions

What Terraform resource creates an Azure NSG?

The azurerm_network_security_group resource creates an Azure NSG.

What Terraform resource creates an NSG rule?

The azurerm_network_security_rule resource is commonly used to create individual NSG rules.

Should I define NSG rules inline or separately?

For small examples either can work, but many teams prefer separate rule resources for better readability and control.

What should I learn after Azure NSG Terraform?

A good next step is Route Tables Terraform, subnet and NIC associations, and more advanced topics like private endpoints and hub-and-spoke security patterns.

Summary

Azure NSG Terraform is one of the best practical ways to learn how Azure traffic-filtering policy becomes infrastructure as code. It teaches you how rule logic is expressed, how ports and IPs are matched, and how reusable security foundations are built in real environments.

Official Azure documentation

These official references are useful if you want deeper platform details for Azure NSGs and the Terraform resources used to create them.