About azurerm_subnet address_prefixes

Requirement : I am trying to create a Azure Subnet into an existing VNet.
Issue : I am unable to dynamically get the available IP ranges in the VNet’s address space and assign one available IP to Subnet’s address_prefixes variable.

Trials : I tried using [cidrsubnet(<VNet’s Address Space>, ,netnum, <no. of subents to be created>)

module "subnet"{
    source = "./modules/sub-net"
    name =  "${var.environment}${replace(var.servicename,"-","")}Subnet"
    resource_group_name = data.azurerm_resource_group.existing.name
    virtual_network_name = data.azurerm_virtual_network.existing_vnet.name
    address_prefixes= cidrsubnet(data.azurerm_virtual_network.existing_vnet.address_space[0], 4, 2)]
    network_security_group_id = "${var.network_security_group_id}"
    route_table_id = "${var.route_table_id}
}

But this gives an IP that is already in use, it does not validates the availability of the IP and so terraform apply throws an IP overlap error.
Last option used is to harcode the available IP address as value to address_prefixes but this is not a good standard to follow especially if we are creating for multiple environments and so on.

module "subnet"{
    source = "./modules/sub-net"
    name =  "${var.environment}${replace(var.servicename,"-","")}Subnet"
    resource_group_name = data.azurerm_resource_group.existing.name
    virtual_network_name = data.azurerm_virtual_network.existing_vnet.name
    address_prefixes = ["1XX.20.1XX.2XX/28"]
    network_security_group_id = "${var.network_security_group_id}"
    route_table_id = "${var.route_table_id}"
   
}

Question : Is there any way to first get the available IP ranges in a VNet address space and then assign one of them to SubNet’s address prefixes.

Terraform can certainly ‘compute’ the appropriate CIDR networks dynamically but you need to set the inputs and write your code in such a way that this is repeatable and predicable (idempotent) to avoid the issue you are seeing (and others that I will cover below).
You will need to manage your IP addressing allocation somewhat outside of Terraform, so that you define what ranges are used for what and why such that you can feed this information into your module and/or calculate the required range in an idempotent manner.

Firstly:

It looks like you have misunderstood the cidrsubnet function definition:
cidrsubnet(prefix, newbits, netnum)

prefix is the ‘supernet’ you are calculating your subnets within
newbits is the additional network bits to add to the prefix to calculate the new subnets
netnum is the network number (eg. the Nth sub-network) to return the prefix for.

So as an example, subnetting 172.16.0.0/16 into /24 networks:

prefix = 172.16.0.0/16
newbits = 8 (as 16+8 = 24)

netnum = 0 (the network numbers are 0 indexed so this is the first network)
result = “172.16.0.0/24”

netnum = 1
result = “172.16.1.0/24”

netnum=23
result=“172.16.23.0/24”

If you were using this function in a module to create new subnets then you need to know what network number you need (and therefore which are already in use)
This should be pretty straightforward if your module is completely managing the VNET and its subnets - as you will already have that information in some form in your module (controlled by variables or available via the state of the existing subnet resources you have deployed).
Remember Terraform only knows about resources (including subnets) that it is managing, or that you explicitly get the information for via a data resource. It does not automagically know if someone has gone and dropped in a subnet via the portal.

There is a way you could derive this using data resources and some functions, however this is not going to be idempotent. Take the following given this scenario:

Starting point:
Vnet 10.0.0.0/8
subnets in use [10.0.0.0/24, 10.0.1.0/24]

  1. You apply your module
  2. which derives the next available /24 network number (10.0.2.0/24)
  3. The module creates the subnet
  4. The module then deploys resources into the subnet

subnets NOW in use [10.0.0.0/24, 10.0.1.0/24, 10.0.2.0/24]

  1. You re-apply the same module without any changes to config or code
  2. Which derives the next available /24 network number (10.0.3.0/24)
  3. The module now sees that the subnet resource it was managing has a changed IP
  4. The Plan graph indicates that the subnet address range should be updated and any other knock-on effects to other resources and updates in-place or (worse) destroy+recreates those resources to take into account the subnet change

subnets NOW in use [10.0.0.0/24, 10.0.1.0/24, 10.0.3.0/24]

This scenario would repeat each time you applied a module that derived the IPs in this manner. Resulting in a state that would never be stable due to the lack of idempotency.

In short - pass to, or set in, your module explicit networks (either via network number for for the cidr functions to work with, or explicit CIDR network notations) such that a created network will always be created with the same range unless you explicitly change the configuration.

Hope that helps

Happy Terraforming