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
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 thevnet_hub
resource, while some others were created with theazurerm_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
brings me nothing. Without that
lifecycle
block, and all my subnets created with theazurerm_subnet
resource, I never get my subnets destroyed. I can therefore happily update my infrastructure without having to integrate that block in my configuration.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 theprevent_destroy
attribute totrue
in theazurerm_subnet
resource block to prevent it from being deleted.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:
Terrform Apply:
When I try to add & update the subnet, it does not delete the existing subnet.
Updating Subnet IP Prefix
Adding another subnet in the same Vnet
When I check the same in the portal, it got updated without destroying existing resources as below.