I have an NIC configured like this
resource "azurerm_network_interface" "nic" {
name = "nic1"
location = "West Europe"
resource_group_name = "MyRG"
enable_ip_forwarding = true
ip_configuration {
name = "ipconfig1"
subnet_id = azurerm_subnet.publicsubnet.id
private_ip_address_allocation = "Static"
private_ip_address = "10.0.0.10"
public_ip_address_id = azurerm_public_ip.ClusterPublicIP.id
}
}
Which has a public IP assigned and is eventually used by a Firewall in Azure. However, I would like to add multiple IP-Addresses to this NIC. The IP-Addresses should be added, when I create an application. So I am able to create the public IP with:
resource "azurerm_public_ip" "pip" {
allocation_method = "Static"
location = "West Europe"
name = "pip-${var.appname}"
resource_group_name = var.rgName
}
However, I could not find any resources in the azurerm provider which would allow me to add it to the previously defined nic. The ip_configuration block must be embedded in azurerm_network_interface.
So I tried it with the azapi provider as follows:
resource "azapi_update_resource" "add_pip_to_forti" {
type = "Microsoft.Network/networkInterfaces@2023-06-01"
resource_id = var.nicResourceId
body = jsonencode({
properties = {
ipConfigurations = [
{
name = "assignedByTF"
etag = "W/\"50b5631e-6e90-4196-a2df-d5c280c41b73\""
type = "Microsoft.Network/networkInterfaces/ipConfigurations"
properties = {
privateIPAddress = "10.0.0.200"
privateIPAllocationMethod = "Static"
publicIPAddress = {
id = azurerm_network_interface.nic.id
}
subnet = {
id = "/subscriptions/my-sub/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/hub-europe/subnets/pubsub"
}
primary = false
privateIPAddressVersion = "IPv4"
}
}
]
}
})
}
However, this would delete the existing configuration on the nic, and fails as the primary ip_configuration cannot be deleted.
The question is:
(How) Can the block resource "azapi_update_resource" "add_pip_to_forti"
to extend the array and keep existing values by using terraform?
Thanks.
While not really a nice solution, it's working. What I ended up doing is the following:
data "azurerm_network_interface" "nic_before_change" {
name = "nic1"
resource_group_name = "MyRG"
}
resource "null_resource" "assign_ipconfig_to_nic" {
depends_on = [data.azurerm_network_interface.nic_before_change]
triggers = {
"subscription_id" = "MySub"
"ResourceGroup" = "MyRg"
"nic_name" = "nic1"
"public_ip_id" = azurerm_public_ip.pip.id "ipconfig_name" = local.new_ip_config_name
"evaluate_and_recreate_if_necessary" = length([for ipconfig in data.azurerm_network_interface.nic_before_change.ip_configuration : ipconfig.private_ip_address if ipconfig.name == local.new_ip_config_name]) == 1 # as this value is false in the first run, but true in all others (except if the resource was modified in the hub), the script must evaluate if the resource needs to be added again or not. Unfortunately, this will show a modification being made to this resource in the first two runs (or actually every two runs after which the ipconfig may have been removed from the nic for whichever reason). However, as the script handles the second run to do nothing, it's not an issue - while still not being nice.
}
provisioner "local-exec" {
when = create
command = <<EOF
az account set --subscription ${self.triggers.subscription_id}
free_ip=$(az network vnet subnet list-available-ips --resource-group rg-hub-europe --vnet-name hub-europe -n publicSubnet --query "[0]" | grep -oP '(?<=\").*(?=\")') # Get free in the subnet
recreate_resource=$(az network nic ip-config list -g rg-hub-europe --nic-name activeport2 --query "[].name" | grep ${self.triggers.ipconfig_name} -c)
if [ "$recreate_resource" -eq 0 ] # only (re-)create if a config with the name does not already exist
then
az network nic ip-config create -g ${self.triggers.ResourceGroup} -n ${self.triggers.ipconfig_name} --nic-name ${self.triggers.nic_name} --make-primary false --public-ip-address ${self.triggers.public_ip_id} --private-ip-address-version IPv4 --private-ip-address $free_ip
fi
EOF
}
# There is a chance the resource was destroyed by other means - don't provoke an error when it's no longer there on resource destruction
provisioner "local-exec" {
when = destroy
command = <<EOF
az account set --subscription ${self.triggers.subscription_id}
resource_still_exists=$(az network nic ip-config list -g rg-hub-europe --nic-name activeport2 --query "[].name" | grep ${self.triggers.ipconfig_name} -c)
if [ "$resource_still_exists" -eq 1 ]
then
az network nic ip-config delete -g ${self.triggers.ResourceGroup} -n ${self.triggers.ipconfig_name} --nic-name ${self.triggers.nic_name}
fi
EOF
}
}
## thanks to the depends_on, the data is only loaded AFTER the null_resource scripts altered it, so we receive the most up to date information about the resource here.
data "azurerm_network_interface" "nic_after_change" {
depends_on = [null_resource.assign_ipconfig_to_nic]
name = "nic1"
resource_group_name = "MyRG"
}
What happens is that I load the current resouce state to nic_before_change
, then via a custom script in the null_resource.assign_ipconfig_to_nic
analyze if the nic needs to be changed. In my usecase, if the ipconfig must be added for which-ever reason. This resource is also responsible for deleting it upon destruction.
Eventually, to be able to reference the nics latest state, I import it again to nic_after_change
. As this depends on null_resource.assign_ipconfig_tonic
, the resource shows the latest state.