I am struggling to create a terraform resource to update a datagroup on an F5/Bigip device. My knowledge/experience with HCL is not at the level where I can code a solution without some assistance.
I have a json structure represented in a file (ipwhitelist.json) as below:
And from this I want to create the resource below, but in a dynamic way so it will create the records by looping through the json structure (to account for this changing).
resource "bigip_ltm_datagroup" "whitelist_ip_datagroup" {
name = "/Common/wipdg"
type = "ip"
record {
data = "client1"
name = "1.1.1.1"
}
record {
data = "client1"
name = "2.2.2.2"
}
record {
data = "client2"
name = "3.3.3.3"
}
record {
data = "client2"
name = "4.4.4.4"
}
}
I am tryingto create the map to hold the document and to then loop through that. I have attempted the following mixing in some pseudo-code, as I just can’t get the correct logic or looping syntax in HCL (so haven’t tried to reproduce my failures).
locals {
datagroup = jsondecode(file("${path.module}/ipwhitelist.json"))
all_clients = [ for client in local.datagroup.clients : client.name]
}
resource "bigip_ltm_datagroup" "whitelist_ip_datagroup" {
name = "/Common/wipdg"
type = "ip"
dynamic "record" {
# for each client in all_clients
# for each ip in client
content {
data = client
name = ip
}
}
}
Can anyone help me out with the correct syntax approach to this? Many thanks.
Hi there - many thanks for your reply. I can see this has got me a lot closer, but I now get the error:
│ Error: Unsupported block type
│
│ on main.tf line 57, in resource "bigip_ltm_datagroup" "whitelist_ip_datagroup":
│ 57: dynamic "ip" {
│
│ Blocks of type "dynamic" are not expected here.
I believe this is because it is writing out an ‘ip’ block, but that does not exist within the underlying resource (Terraform Registry).
I’ve tried to remove this or use an iterator. But it also seems there are issues with direct nesting of for_each loops.
Hi there - and many thanks for your help. Still seems to be an issue which again I’ve tried to debug without success.
╷
│ Error: Missing required argument
│
│ on test.tf line 6, in resource "bigip_ltm_datagroup" "whitelist_ip_datagroup":
│ 6: content {
│
│ The argument "name" is required, but no definition was found.
╵
╷
│ Error: Missing required argument
│
│ on test.tf line 6, in resource "bigip_ltm_datagroup" "whitelist_ip_datagroup":
│ 6: content {
│
│ The argument "name" is required, but no definition was found.
╵
╷
│ Error: Unsupported block type
│
│ on test.tf line 7, in resource "bigip_ltm_datagroup" "whitelist_ip_datagroup":
│ 7: dynamic "ip" {
│
│ Blocks of type "ip" are not expected here.
╵
╷
│ Error: Unsupported block type
│
│ on test.tf line 7, in resource "bigip_ltm_datagroup" "whitelist_ip_datagroup":
│ 7: dynamic "ip" {
│
│ Blocks of type "ip" are not expected here.
Sorry my mistake, i understand now the problem of the inner block, because we don’t had a second block (we had only one block to make dynamic called “record”), so we need to change
local.all_clients from 2 levels loop to one level.
I will test again with “flatten” function, wait a moment
Because client names is not unique by ip’s, we cannot work with maps, we need to work with sets, because set guarantee order of the elements.
I merged information about client name and your ip with “_” caracter, you can change for other caracter, but i needed because we need the information together and we need separate inner dynamic iteration.
flatten is a function to solve a problem with array of array, and change to single array with distinct elements of the arrays.
locals {
datagroup = jsondecode(file("${path.module}/ipwhitelist.json"))
all_clients_with_ips = toset(flatten([for k, v in local.datagroup.clients : [for k1 in v.ipaddress : "${v.name}_${k1}"]]))
}
resource "bigip_ltm_datagroup" "whitelist_ip_datagroup" {
name = "/Common/wipdg"
type = "ip"
dynamic "record" {
for_each = local.all_clients_with_ips
content {
name = split("_", record.key)[0]
data = split("_", record.key)[1]
}
}
}
Don’t know if it helps, but maybe see if any of what I posted in this other thread helps? This general approach (making a simple / self-contained case I can play around with with just the data structure and an output) has been really helpful for me in the past.
You could also see if merge() with maps and the spread operator helps at all for your use case (I haven’t looked too carefully at your exact case). But high level, sometimes with Terraform, half the battle is figuring out what the input data structure really should look like, and then figure out to wrangle it into the form you need it in (which is often different).