Step-by-Step Guide to Optimising Azure NSG Rules creation with Terraform: try Function and for_each Explained

This guide looks at Optimising Azure NSG Rules Creation with Terraform. We’ll look at how to use the try function with for_each for Terraform NSG Rule Configuration and all arguments for azurerm_network_security_rule, providing a Step-by-Step Terraform NSG Guide for optimising Terraform Azure NSG Rules.

A follow on from my popular blog post: Network Security Group Rule Creation using Terraform

Why Use the try Function in Terraform for NSG Rules?

The try function in Terraform is essential for managing various possible configurations by allowing you to specify multiple arguments and use the first non-erroring value. This is particularly useful for handling optional arguments in Terraform NSG Rule Configuration.

In the context of Network Security Group (NSG) rule creation, it is common to encounter multiple optional values, such as source_port_range, source_port_ranges, destination_address_prefix, and destination_address_prefixes. By leveraging the try function, you can effectively manage these cases where values might not always be provided, making your configurations more robust and adaptable.

Code for Optimising Azure NSG Rules creation with Terraform

To demonstrate Advanced Azure NSG Rule Creation with Terraform, let’s start by setting up a Resource Group and NSG that will be used in our ruleset:

resource "azurerm_resource_group" "nsgrg" {
  name     = "tamops-nsg-rg"
  location = "uksouth"
}

resource "azurerm_network_security_group" "nsg" {
  name                = "tamopsnsg"
  location            = azurerm_resource_group.nsgrg.location
  resource_group_name = azurerm_resource_group.nsgrg.name
}

In this example, we create an Azure Resource Group and Network Security Group (NSG). These resources will serve as the foundation for our NSG ruleset.

How to Use for_each and try for Dynamic NSG Rule Creation

With the for_each and try functions, we can create dynamic and flexible NSG rules. Here’s how you can configure the azurerm_network_security_rule resource:

resource "azurerm_network_security_rule" "advanced_rules" {
  for_each                                   = local.nsgrules
  name                                       = each.key
  direction                                  = each.value.direction
  access                                     = each.value.access
  priority                                   = each.value.priority
  protocol                                   = each.value.protocol
  resource_group_name                        = azurerm_resource_group.nsgrg.name
  network_security_group_name                = azurerm_network_security_group.nsg.name
  source_port_range                          = try(each.value.source_port_range, null)
  source_port_ranges                         = try(each.value.source_port_ranges, null)
  destination_port_range                     = try(each.value.destination_port_range, null)
  destination_port_ranges                    = try(each.value.destination_port_ranges, null)
  source_address_prefix                      = try(each.value.source_address_prefix, null)
  source_address_prefixes                    = try(each.value.source_address_prefixes, null)
  destination_address_prefix                 = try(each.value.destination_address_prefix, null)
  destination_address_prefixes               = try(each.value.destination_address_prefixes, null)
  source_application_security_group_ids      = try(each.value.source_application_security_group_ids, null)
  destination_application_security_group_ids = try(each.value.destination_application_security_group_ids, null)
}

Breaking it down:

  • Network Security rule creation: A for_each map will be passed that will contain all the necessary values needed to create the NSG ruleset
  • Using the try function: This is really where the magic happens, using this function throughout will determine which optional arguments to use

Defining NSG Rules with Maps and Key-Value Pairs in Terraform

We can create a map with a number of key-value pairs to define our NSG ruleset, allowing us to create both inbound and outbound rules together:

locals {
  nsgrules = {

    ssh-all = {
      name                       = "ssh-all"
      priority                   = 100
      direction                  = "Inbound"
      access                     = "Allow"
      protocol                   = "Tcp"
      source_address_prefix      = "1.2.3.4/32"
      source_port_range          = "*"
      destination_port_range     = "22"
      source_address_prefix      = "VirtualNetwork"
      destination_address_prefix = "*"
    }

    rdp-bastion = {
      name                         = "rdp-bastion"
      priority                     = 101
      direction                    = "Inbound"
      access                       = "Allow"
      protocol                     = "Tcp"
      source_port_range            = "*"
      destination_port_range       = "3389"
      source_address_prefixes      = ["1.2.3.4/32", "5.6.7.8/32"]
      destination_address_prefixes = ["192.168.10.10/32", "192.168.10.11/32"]
    }

    http-https = {
      name                       = "http-https"
      priority                   = 102
      direction                  = "Inbound"
      access                     = "Allow"
      protocol                   = "Tcp"
      source_port_range          = "*"
      destination_port_ranges    = [80,443]
      source_address_prefixes    = ["1.2.3.4/32", "5.6.7.8/32"]
      destination_address_prefix = "AzureLoadBalancer"
    }

    dns-tcp = {
      name                       = "dns-tcp"
      priority                   = 100
      direction                  = "Outbound"
      access                     = "Allow"
      protocol                   = "Tcp"
      source_port_range          = "*"
      destination_port_range    = "53"
      source_address_prefix      = "*"
      destination_address_prefix = "*"
    }

    dns-udp = {
      name                       = "dns-tcp"
      priority                   = 101
      direction                  = "Outbound"
      access                     = "Allow"
      protocol                   = "Udp"
      source_port_range          = "*"
      destination_port_range    = "53"
      source_address_prefix      = "*"
      destination_address_prefix = "*"
    }

  }

}
  • Notice the use of different optional arguments. For instance, some rules use destination_port_range, while others use destination_port_ranges. This approach makes your configuration dynamic and reusable for various rule cases.

Reviewing Inbound and Outbound Security Rules in Azure

As above shows both inbound and outbound rules for the same NSG, lets review the Azure portal after the Terraform has been successfully ran

Azure NSG Inbound Security Rules

  • Notice the variation of rules in Port, Source and Destination?

Azure NSG Outbound Security Rules

  • Similarly, you will see the outbound rules reflecting the same flexibility and coverage.

By using for_each and try, we can create a flexible, powerful, and reusable set of NSG rules. This method handles multiple optional values efficiently, ensuring that our infrastructure as code is both robust and adaptable.


GitHub repository containing Terraform from this blog post

2 thoughts on “Step-by-Step Guide to Optimising Azure NSG Rules creation with Terraform: try Function and for_each Explained”

Leave a Reply to Thomas ThorntonCancel reply

Discover more from Thomas Thornton Blog

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

Continue reading

Discover more from Thomas Thornton Blog

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

Continue reading