Terraform is an open-source tool used for writing infrastructure as code. This software allows you to create, change and delete infrastructure predictably and safely. In terraform, we have providers which enable us to define the infrastructure for multiple cloud providers and even some local solutions like Virtualbox. Terraform code is written in an HCI language. At first, writing terraform might be a little odd but when you get the hang of it, it becomes effortless to write and read it.

So to head to our problem of having a list of allowed IP ranges we need to integrate into Azure Web App networking to allow traffic to Azure Web App from the allowed range of IPs.

First, let’s define the Yaml file which will hold our ranges.

ip_config:
    - name: firstrange
      description: "First Range"
      cidr: 8.8.8.8/32
      range: 
        start: 8.8.8.8
        end: 8.8.8.8
      priority: 101
    - name: secondrange
      description: "Second Range"
      cidr: 192.168.0.1/24
      range: 
        start: 192.168.0.1
        end: 192.168.0.254
      priority: 102

Now that we have our Yaml file we can switch to terraform.

First, let’s create locals.tf like this

locals {
  ip_restriction = [
    for ip_config in var.authorized_ips : {
      name                      = ip_config["name"]
      ip_address                = try(ip_config["cidr"], null)
      virtual_network_subnet_id = try(ip_config["virtual_network_subnet_id"], null)
      service_tag               = try(ip_config["service_tag"], null)
      priority                  = ip_config["priority"]
      action                    = "Allow"
      headers                   = try(ip_config["headers"], null)
    }
  authorized_ips = yamldecode(file("../shared/ip_ranges.yaml"))["ip_config"]
  ]

In the first part of locals.tf a file, we are creating a mapping of values to variables. In authorized_ips we use yamldecode function to pars our ip_ranges Yaml file.

Now we can create our main.tf which will be in charge of creating an Azure Web App. I will give here an example of configuration from terraform documentation.

provider "azurerm" {
  features {}
}

resource "azurerm_resource_group" "example" {
  name     = "example-resources"
  location = "West Europe"
}

resource "azurerm_service_plan" "example" {
  name                = "example"
  resource_group_name = azurerm_resource_group.example.name
  location            = azurerm_resource_group.example.location
  os_type             = "Linux"
  sku_name            = "P1v2"
}

resource "azurerm_linux_web_app" "example" {
  name                = "example"
  resource_group_name = azurerm_resource_group.example.name
  location            = azurerm_service_plan.example.location
  service_plan_id     = azurerm_service_plan.example.id

  site_config {}
  
  ip_restriction     = local.authorized_ips
}

Let’s extend our solution so it is able to allow services.

First, we need to extend the Yaml file

ip_config:
    - name: firstrange
      description: "First Range"
      cidr: 8.8.8.8/32
      range: 
        start: 8.8.8.8
        end: 8.8.8.8
      priority: 101
    - name: secondrange
      description: "Second Range"
      cidr: 192.168.0.1/24
      range: 
        start: 2192.168.0.1
        end: 192.168.0.254
      priority: 102

services:
  - name: AzureCloud
    description: "Allow Azure Devops to deploy"
    service_tag: "AzureCloud"
    priority: 401

The next thing that needs to be changed is authorized_ips from locals.tf

locals {
  ip_restriction = [
    for ip_config in var.authorized_ips : {
      name                      = ip_config["name"]
      ip_address                = try(ip_config["cidr"], null)
      virtual_network_subnet_id = try(ip_config["virtual_network_subnet_id"], null)
      service_tag               = try(ip_config["service_tag"], null)
      priority                  = ip_config["priority"]
      action                    = "Allow"
      headers                   = try(ip_config["headers"], null)
    }

  authorized_ips     = concat(yamldecode(file("../shared/ip_ranges.yaml"))["ip_config"], yamldecode(file("../shared/ip_ranges.yaml"))["services"])
}

That would be the whole configuration.

Hope it helps you in your adventures in terraform land 🙂