Network Security Group Rule Creation using Terraform

In this blog post I am going to create a set of Network Security Group rules in Terraform using the resource azurerm_network_security_rule and rather than copying this resource multiple times I will show how you can iterate over the same resource multiple times using for_each meta-argument in Terraform.

By default, a resource block configures one real infrastructure object. However, sometimes you want to manage several similar objects, such as a fixed pool of compute instances. Terraform has two ways to do this: count and for_each.

The for_each meta-argument accepts a map or a set of strings, and creates an instance for each item in that map or set. Each instance has a distinct infrastructure object associated with it (as described above in Resource Behavior), and each is separately created, updated, or destroyed when the configuration is applied.

Read further here

terraform.io

I decided to use a map so I can pass potentially separate settings to each Network Security Group (NSG) rule while using the key each time to create the rule.

azurerm_network_security_rule from Terraform

Lets look at azurerm_network_security_rule from terraform.io

resource "azurerm_resource_group" "example" {
  name     = "acceptanceTestResourceGroup1"
  location = "West US"
}

resource "azurerm_network_security_group" "example" {
  name                = "acceptanceTestSecurityGroup1"
  location            = azurerm_resource_group.example.location
  resource_group_name = azurerm_resource_group.example.name
}

resource "azurerm_network_security_rule" "example" {
  name                        = "test123"
  priority                    = 100
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "*"
  source_address_prefix       = "*"
  destination_address_prefix  = "*"
  resource_group_name         = azurerm_resource_group.example.name
  network_security_group_name = azurerm_network_security_group.example.name
}

For this blog post, I will keep the attributes/arguments as above. As previously mentioned; I could create multiple resources of azurerm_network_security_rule to deploy multiple NSG rules but over time your Terraform file would become quite congested depending on the size of environment you will be deploying.

Configuration Time

Inside locals.tf I will create a map nsgrules that will have 3 rules rdp, sql & http with the required attributes/arguments configured for each

locals.tf

locals { 
nsgrules = {
  
    rdp = {
      name                       = "rdp"
      priority                   = 100
      direction                  = "Inbound"
      access                     = "Allow"
      protocol                   = "Tcp"
      source_port_range          = "*"
      destination_port_range    = "3389"
      source_address_prefix      = "VirtualNetwork"
      destination_address_prefix = "*"
    }

    sql = {
      name                       = "sql"
      priority                   = 101
      direction                  = "Inbound"
      access                     = "Allow"
      protocol                   = "Tcp"
      source_port_range          = "*"
      destination_port_range     = "1433"
      source_address_prefix      = "SqlManagement"
      destination_address_prefix = "192.168.2.0/24"
    }

    http = {
      name                       = "http"
      priority                   = 201
      direction                  = "Inbound"
      access                     = "Allow"
      protocol                   = "Tcp"
      source_port_range          = "*"
      destination_port_range     = "80"
      source_address_prefix      = "*"
      destination_address_prefix = "192.168.2.0/24"
    }
  }

}

Now back to my Terraform file, in this case <strong>main.tf</strong>

  • Notice the reference of for_each used (will loop over the map nsgrules created in locals.tf)
  • Name has to be unique, using the key for each rule
  • resource_group_name & <strong>network_security_group_name</strong> is static and needs to be created prior, will include this in the final snippet of the code
resource "azurerm_network_security_rule" "testrules" {
  for_each                    = local.nsgrules 
  name                        = each.key
  direction                   = each.value.direction
  access                      = each.value.access
  priority                    = each.value.priority
  protocol                    = each.value.protocol
  source_port_range           = each.value.source_port_range
  destination_port_range      = each.value.destination_port_range
  source_address_prefix       = each.value.source_address_prefix
  destination_address_prefix  = each.value.destination_address_prefix
  resource_group_name         = azurerm_resource_group.tamopsrg.name
  network_security_group_name = azurerm_network_security_group.tamopsnsg.name
}

Full code in main.tf

resource "azurerm_resource_group" "tamopsrg" {
  name     = "tamopsrg"
  location = "eastus2"
}

resource "azurerm_network_security_group" "tamopsnsg" {
  name                = "tamopstest"
  location            = "eastus2"
  resource_group_name = azurerm_resource_group.tamopsrg.name

}

resource "azurerm_network_security_rule" "testrules" {
  for_each                    = local.nsgrules 
  name                        = each.key
  direction                   = each.value.direction
  access                      = each.value.access
  priority                    = each.value.priority
  protocol                    = each.value.protocol
  source_port_range           = each.value.source_port_range
  destination_port_range      = each.value.destination_port_range
  source_address_prefix       = each.value.source_address_prefix
  destination_address_prefix  = each.value.destination_address_prefix
  resource_group_name         = azurerm_resource_group.tamopsrg.name
  network_security_group_name = azurerm_network_security_group.tamopsnsg.name
}

Time to deploy

Deploying the above Terraform code will create:-

  • Resource Group:- tamopsrg
  • Network Security Group:- tamopstest
  • Network Security Group Ruleset from locals.tf

Reviewing NSG tamopstest you can see the ruleset from locals.tf has been applied successfully

Successfully deployed! – Hopefully an insight into how you can lyou can iterate over the same resource multiple times using for_each meta-argument in Terraform.

13 thoughts on “Network Security Group Rule Creation using Terraform”

  1. Hi Thomas.
    Thanks for this wonderful piece.

    Is there a way to have multiple nsg-rules declared in locals.tf (probably) and only use a few (maybe 3 out of 7 rules) rather than all; for a specific azurerm_network_security_group

    Thanks

    Reply
  2. Hello Thomas,

    I got close to 50 ports which should have outbound rules and another 30 ports for inbound rules, is there a way to simply the code instead of providing all the ports in local file?

    I am wondering if we can have only one per direction and adding the ports in that variable, but not sure if that is possible. Can you please suggest?

    Regards,
    Janakiraman

    Reply
    • Hi Janakiram,

      Thanks for the comment. Sure you could split the below into two resources?

      Notice how I am adding direction = “inbound” & direction = “outbound?” . The for_each has been changed to include both local.nsgrulesinbound & local.nsgrulesoutbound

      resource “azurerm_network_security_rule” “inbound” {
      for_each = local.nsgrulesinbound
      name = each.key
      direction = “inbound”
      access = each.value.access
      priority = each.value.priority
      protocol = each.value.protocol
      source_port_range = each.value.source_port_range
      destination_port_range = each.value.destination_port_range
      source_address_prefix = each.value.source_address_prefix
      destination_address_prefix = each.value.destination_address_prefix
      resource_group_name = azurerm_resource_group.tamopsrg.name
      network_security_group_name = azurerm_network_security_group.tamopsnsg.name
      }

      resource “azurerm_network_security_rule” “outbound” {
      for_each = local.nsgrulesoutbound
      name = each.key
      direction = “outbound”
      access = each.value.access
      priority = each.value.priority
      protocol = each.value.protocol
      source_port_range = each.value.source_port_range
      destination_port_range = each.value.destination_port_range
      source_address_prefix = each.value.source_address_prefix
      destination_address_prefix = each.value.destination_address_prefix
      resource_group_name = azurerm_resource_group.tamopsrg.name
      network_security_group_name = azurerm_network_security_group.tamopsnsg.name
      }

      Let me know how you proceed

      Thanks

      Thomas

      Reply
      • Hello Thomas,

        Thanks for the quick update.
        I assume still in the locals.tf file, we have to provide with the all the details for my 100 ports. Is my understanding is correct? Is there a better way to handle it? Variable is difficult to manage and i am afraid it could lead to manual errors.

        locals {
        nsgrules = {

        rdp = {
        name = “rdp”
        priority = 100
        direction = “Inbound”
        access = “Allow”
        protocol = “Tcp”
        source_port_range = “*”
        destination_port_range = “3389”
        source_address_prefix = “VirtualNetwork”
        destination_address_prefix = “*”
        }

        …….

        Regards,
        Janakiraman

  3. Hello Thomas,

    The below code worked for me, almost similar to your suggestion. In my local variable, i have provided only the ports, priority and name alone. Thanks again for your support.

    # NSG rule to open ports for Web dispatcher
    resource “azurerm_network_security_rule” “webrule” {
    count = length(local.nsg-ports.webin)
    name = local.nsg-ports.webin[count.index].name
    priority = local.nsg-ports.webin[count.index].priority
    direction = “Inbound”
    access = “Allow”
    protocol = “Tcp”
    source_port_range = “*”
    destination_port_range = “*”
    source_address_prefix = “*”
    destination_address_prefix = “*”
    resource_group_name = “AUTOMATION_TESTRG”
    network_security_group_name = azurerm_network_security_group.web.name
    }

    # NSG rule to open ports for Web dispatcher
    resource “azurerm_network_security_rule” “webruleout” {
    count = length(local.nsg-port.web)
    name = local.nsg-port.web[count.index].name
    priority = local.nsg-port.web[count.index].priority
    direction = “Outbound”
    access = “Allow”
    protocol = “Tcp”
    source_port_range = “*”
    destination_port_range = “*”
    source_address_prefix = “*”
    destination_address_prefix = “*”
    resource_group_name = “AUTOMATION_TESTRG”
    network_security_group_name = azurerm_network_security_group.web.name
    }

    Regards,
    Janakiraman

    Reply

Leave a comment

Discover more from Thomas Thornton Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading