e-breuninger / terraform-provider-netbox

Terraform provider to interact with Netbox
https://registry.terraform.io/providers/e-breuninger/netbox/latest/docs
Mozilla Public License 2.0
173 stars 117 forks source link

[help] reference cycle when trying to create a VM and populating netbox at the same time. #613

Open vhsantos opened 1 week ago

vhsantos commented 1 week ago

Hi there,

Actually, Im creating some VMs (vsphere) and when the VMs has a "static IP" it works well using it with the resource netbox_ip_address, netbox_virtual_machine and netbox_interface, for example:

resource "foreman_host" "hosts" {
  for_each = var.hosts
  name               = each.value.hostname
  ip = each.value.ip
}
resource "netbox_interface" "this" {
  for_each = foreman_host.hosts
  name        = each.value.interfaces_attributes[0].identifier
  enabled     = true
  mac_address = each.value.interfaces_attributes[0].mac
  mode        = "access"
  virtual_machine_id = netbox_virtual_machine.this[each.key].id
}
resource "netbox_virtual_machine" "this" {
  for_each = foreman_host.hosts
  cluster_id = data.netbox_cluster.cluster.id
  name       = each.value.fqdn
  disk_size_gb = each.value.disk[0].size
  memory_mb    = each.value.memory
  vcpus        = each.value.cpu
}
resource "netbox_ip_address" "this" {
  for_each                     = foreman_host.hosts
  ip_address                   = "${each.value.interfaces_attributes[0].ip}/24"
  status                       = "active"
  dns_name                     = each.value.fqdn
  virtual_machine_interface_id = netbox_interface.this[each.key].id
}

The issue come when I try to use the resource "netbox_available_ip_address", because in this case it always generate a reference cycle, for example:

locals {
  # Ensure valid IP addresses
  hosts_without_ip = { for k, v in var.hosts : k => v if !try(length(v.ip) > 0, false) }
}
resource "foreman_host" "hosts" {
  for_each = var.hosts
  name               = each.value.hostname
    ip = try(length(each.value.ip) > 0, false) ? each.value.ip : split("/", lookup(netbox_available_ip_address.this, each.key, null).ip_address)[0]
}
resource "netbox_available_ip_address" "this" {
  for_each  = local.hosts_without_ip
  prefix_id = data.netbox_prefix.auxiliar_systems.id
  status    = "active"
  # dns_name                     = each.value.fqdn
  virtual_machine_interface_id = netbox_interface.this[each.key].id
}
resource "netbox_interface" "this" {
  for_each = foreman_host.hosts
  name        = each.value.interfaces_attributes[0].identifier
  enabled     = true
  mac_address = each.value.interfaces_attributes[0].mac
  mode        = "access"
  virtual_machine_id = netbox_virtual_machine.this[each.key].id
}

I have tried to use the netbox_available_ip_address to get the free IP and create the VM, then use the netbox_ip_address to associate it with the virtual_machine_interface_id, but it complain that the IP is already been used. :-(

My goal is basically to use the free_ip to provising the VM and fill the netbox with as much info that come from the VM created.

any suggestion how continue here ?? Thanks in advance.

vhsantos commented 1 week ago

I saw the #602 with similar background, but not sure when (or if it will be merged). Anyway, someone know why there is not data resource for the netbox_available_ip_address ?? In this case, should be easy to get the next free ip and kind of use it with the netbox_ip_address.

I know that it can maybe generate a race condition as well trying to create multiples objects at the same time, but maybe can works in some scenarios.

skoppe commented 2 days ago

@vhsantos good to see I am not the only one.

My PR has one last issue, namely that it interacts unfavorably with the underlying netbox_ip_address or netbox_available_ip_address resource.

Any assignment can be left undone after changes to either ip resource.

What needs to be done is to add an external_assignment boolean to both ip resources, so it knows to avoid updating/clearing any assignment.