skip to Main Content

In my Azure infrastructure, I have a policy that prevents me from creating subnets without a network security group. Because I want to create a flexible postgres server, I need to create a delegated subnet. In order to create a delegated subnet, I need to separate subnet from vnet creation, i.e. I need to create the subnet in a separate resource than the vnet. Theoretically, I should do this:

resource "azurerm_virtual_network" "vnet_hub" {
  name                = "HubVnet"
  location            = var.location
  resource_group_name = var.resource_group_name
  address_space       = ["10.0.0.0/21"]
}

resource "azurerm_subnet" "db" {
  name                 = "db-subnet"
  resource_group_name  = var.resource_group_name
  virtual_network_name = azurerm_virtual_network.vnet_hub.name
  address_prefixes     = ["10.0.3.0/24"]

  delegation {
    name = "delegation"

    service_delegation {
      name    = "Microsoft.DBforPostgreSQL/flexibleServers"
      actions = ["Microsoft.Network/virtualNetworks/subnets/join/action"]
    }
  }
}

resource "azurerm_network_security_group" "db" {
  name                = "db-nsg"
  location            = var.location
  resource_group_name = var.resource_group_name

  security_rule {
    name                       = "test123"
    priority                   = 100
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "*"
    source_address_prefix      = "*"
    destination_address_prefix = "*"
  }
}

resource "azurerm_subnet_network_security_group_association" "db" {
  subnet_id                 = azurerm_subnet.db.id
  network_security_group_id = azurerm_network_security_group.db.id
}

However, that is not possible to do, because the delegated subnet is first created without network security group, and only then it is associated with an NSG. Azure complains that no subnet can be created without an NSG because of the azure policy we have put in place. We don’t want to get rid of that policy.

So the only solution I have been able to come up with until now is the following:

resource "azurerm_virtual_network" "vnet_hub" {
  name                = "HubVnet"
  location            = var.location
  resource_group_name = var.resource_group_name
  address_space       = ["10.0.0.0/21"]
}

resource "azurerm_network_security_group" "db" {
  name                = "db-nsg"
  location            = var.location
  resource_group_name = var.resource_group_name

  security_rule {
    name                       = "test123"
    priority                   = 100
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "*"
    source_address_prefix      = "*"
    destination_address_prefix = "*"
  }
}

resource "null_resource" "database_subnet" {
  depends_on = [azurerm_virtual_network.vnet_hub]

  provisioner "local-exec" {
    command     = format("%s/%s", path.module, "add_database_subnet.sh")
    interpreter = ["sh"]
    environment = {
      RESOURCE_GROUP_NAME       = var.resource_group_name
      SUBNET_NAME               = "db-subnet"
      VNET_NAME                 = azurerm_virtual_network.vnet_hub.name
      ADDRESS_PREFIX            = "10.0.3.0/24"
      NETWORK_SECURITY_GROUP_ID = azurerm_network_security_group.db.id
    }
  }
}

where the add_database_subnet.sh contains

#! /bin/sh

az network vnet subnet create 
  --name "${SUBNET_NAME}" 
  --vnet-name "${VNET_NAME}" 
  --resource-group "${RESOURCE_GROUP_NAME}" 
  --address-prefixes "${ADDRESS_PREFIX}" 
  --network-security-group "${NETWORK_SECURITY_GROUP_ID}" 
  --service-endpoints "Microsoft.Storage" 
  --delegations "Microsoft.DBforPostgreSQL/flexibleServers"

While that works when I run terraform deploy once, it doesn’t when I run it twice. The db-subnet gets deleted when I run terraform deploy a second time, leaving me with no more subnet for my database.

I want to be able to run multiple terraform deploy for the sake of updating my infrastructure, for example. How should I proceed? what can I do to make it happen? Is there a way without a shell script?

2

Answers


  1. Chosen as BEST ANSWER

    So, in the end, it turns out I made a mistake that is not shown in my initial post. I created multiple subnets inside of my vnet_hub VNet, some of which were in-lined in the vnet_hub resource, while some others were created with the azurerm_subnet resource. As documented by terraform, this is not supported, hence my issue.

    However, contrary to what is suggested in this answer to my post, using the

    lifecycle  {
      prevent_destroy = true
    }
    

    brings me nothing. Without that lifecycle block, and all my subnets created with the azurerm_subnet resource, I never get my subnets destroyed. I can therefore happily update my infrastructure without having to integrate that block in my configuration.


  2. I tried to reproduce the same in my environment and below is the result

    First, I created Virtual Network and the Network Security Group resources, Then I create the delegated Subnet resource and associate it with the VNet and NSG resources using Terraform.

    You can use Terraform lifecycle meta-argument to prevent the deletion of the subnet resource. You can set the prevent_destroy attribute to true in the azurerm_subnet resource block to prevent it from being deleted.

     lifecycle  {
            prevent_destroy = true
            }
    

    prevent_destroy to true, terraform will not remove the subnet resource even if it believes that it is no longer required. This should solve the issue you are encountering when running Terraform multiple times.

    Terraform code:

        provider  "azurerm"  {
        features  {}
        }
        
        #Resource Group Creation
        
        resource  "azurerm_resource_group"  "sqlrg"  {
        name = "test-resources"
        location = "eastus"
        }
        
        #Virtual Network creation
        
        resource  "azurerm_virtual_network"  "sqlvnet"  {
        name = "example-security-group"
        address_space = ["10.0.0.0/16"]
        location = azurerm_resource_group.sqlrg.location
        resource_group_name = azurerm_resource_group.sqlrg.name
        }
             #NSG Creation
             
        resource  "azurerm_network_security_group"  "sqlnsg"  {
        name = "example-security-group"
        location = azurerm_resource_group.sqlrg.location
        resource_group_name = azurerm_resource_group.sqlrg.name
        
        # Subnet Creation
        
        resource  "azurerm_subnet"  "sqlsubnet"  {
        name = "example-security-group"
        resource_group_name = azurerm_resource_group.sqlrg.name
        virtual_network_name = azurerm_virtual_network.sqlvnet.name
        address_prefixes = ["10.0.5.0/24"]
        delegation  {
        name = "delegation"
        service_delegation  {
        name = "Microsoft.DBforPostgreSQL/flexibleServers"
        actions = ["Microsoft.Network/virtualNetworks/subnets/join/action"]
        }
        }
        lifecycle  {
        prevent_destroy = true
        }
        }
     } 
     resource  "azurerm_subnet_network_security_group_association"  "sqlnic"  {
        subnet_id = azurerm_subnet.sqlsubnet.id
        network_security_group_id = azurerm_network_security_group.sqlnsg.id
        }
    

    Terrform Apply:

    enter image description here

    When I try to add & update the subnet, it does not delete the existing subnet.

    Updating Subnet IP Prefix

    enter image description here

    Adding another subnet in the same Vnet

    enter image description here

    When I check the same in the portal, it got updated without destroying existing resources as below.

    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search