terraform-provider-infoblox
terraform-provider-infoblox copied to clipboard
query multiple IPs in loop count
Hi,
is there a way to support count loop returning more than one next available IP ? I am aware that Infoblox API supports it (&num=X) https://community.infoblox.com/t5/API-Integration/Get-Next-Available-IP-and-Reserve/td-p/8548
my problem, I want to ask for the next X available IPs and create an A record for all of them. On the example below in my case count = 4, but what happens is that I get 4x the same IP, instead of unique 4 next available IPs.
resource "infoblox_ip" "tunnel_ips" { count = "4" cidr = "192.168.0.0/16" }
resource "infoblox_record" "vpn_host_internal" { count = "4" value = "${element(infoblox_ip.tunnel_ips.*.ipaddress, count.index)}" name = "test-{count.index +1}" domain = "acme.com" type = "A" ttl = 3600 }
I could work around creating static entries, but in my case count is a dynamic variable, so the code needs to be able to span dynamic count
-
module.stack.router.infoblox_record.vpn_host_internal.0 domain: "acme.com" name: "test-1" ttl: "3600" type: "A" value: "192.168.0.40"
-
module.stack.router.infoblox_record.vpn_host_internal.1 domain: "acme.com" name: "test-2" ttl: "3600" type: "A" value: "192.168.0.40"
-
module.stack.router.infoblox_record.vpn_host_internal.2 domain: "acme.com" name: "test-3" ttl: "3600" type: "A" value: "192.168.0.40"
-
module.stack.router.infoblox_record.vpn_host_internal.3 domain: "acme.com" name: "test-4" ttl: "3600" type: "A" value: "192.168.0.40"
Is there anyway to stop it doing this ? Maybe my terraform knowledge is lacking but it seems the way terraform resolves resources can result in you giving the same IP? Even with a count ability implemented in to infoblox_record, if you use the resource more than once you just end up with the same IPs.
master.tf resource "infoblox_ip" "jenkins_master_ip" { cidr = "${var.network}" }
resource "infoblox_record" "jenkins_master_arecord" { value = "${infoblox_ip.jenkins_master_ip.ipaddress}" name = "jenkins-master" domain = "${var.hostname_domain}" type = "A" ttl = 3600 }
output "jenkins_master_ip" { value = "${infoblox_ip.jenkins_master_ip.ipaddress}" }
slave.tf resource "infoblox_ip" "jenkins_slave_ip" { cidr = "${var.network}" }
resource "infoblox_record" "jenkins_slave_arecord" { value = "${infoblox_ip.jenkins_slave_ip.ipaddress}" name = "jenkins-slave" domain = "${var.hostname_domain}" type = "A" ttl = 3600 }
output "jenkins_slave_ip" { value = "${infoblox_ip.jenkins_slave_ip.ipaddress}" }
infoblox_ip.jenkins_slave_ip: Creating...
cidr: "" => "10.208.80.0/20"
ipaddress: "" => "
If you still use this provider in 2019 and stumble upon this issue. There is a way to do this without provider modification. You have to create your resource like this:
variable "ip_range" {
default = "10.0.0.13-10.0.0.15"
}
resource "infoblox_record_host" "internal_ip" {
name = "${var.node_name}${format("%02d", count.index+1)}.${var.dns_suffix}"
configure_for_dns = true
ipv4addr {
address = "func:nextavailableip:${var.ip_range}"
}
count = "${var.node_count}"
}
without creating a resource "infoblox_ip" "internal_range"
Keep in mind that the provider does not yet fully support this syntax! And you have to create a dhcp reservation range before using this!
If you replan it will show up as changed:
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
~ module.gameserver.infoblox_record_host.internal_ip[0]
ipv4addr.0.address: "10.0.0.13" => "func:nextavailableip:10.0.0.13-10.0.0.15"
~ module.gameserver.infoblox_record_host.internal_ip[1]
ipv4addr.0.address: "10.0.0.14" => "func:nextavailableip:10.0.0.13-10.0.0.15"
I'll open a pull request soon to allow this syntax and have a proper state management along with it.
@dn1s I was able to solve this problem based your suggestion, thanks for sharing! However, what do you mean when you say
Keep in mind that the provider does not yet fully support this syntax! And you have to create a dhcp reservation range before using this!
Mine just worked without having to do anything special with DHCP reservation or otherwise.
EDIT:
I think I understand what you mean. The first terraform apply it's fine, but it's subsequent apply that will cause an issue - in that it will keep attaching new IP's to the same host record each time?
@sjwl wrote:
in that it will keep attaching new IP's to the same host record each time?
Yes the next available IP so probably it will flap between two ips for example from 10.10.10.10 to 10.10.10.11 and back.
Mine just worked without having to do anything special with DHCP reservation or otherwise.
Might be the api version. I guess we use different api versions.
@dn1s are you able to output the IP list to a variable to pass to something like VMware for customization?
Never mind figured that part out. I would love to help this implemented. Please let me know how I can help with developing the missing pieces to this provider.
One thing I noticed is that the host record does not create corresponding ptr record. Wonder if that is something we could add.
Hi @JackBracken was wondering if you could add support for getting X number of next free IPs using this provider? That would be super helpful. Thank you!
Hi @codyja I can definitely give it a go, should be straightforward enough. My access to Infoblox is quite limited and sporadic however so it may be some time.
I have exactly the same issue trying to link infoblox to vSphere. It works, but a second apply forces a recreation of the VM(s). @JackBracken I'm not experienced with Go language, but I'm willing to test your code updates in our Infoblox grid
resource "infoblox_record_host" "host" {
count = "${length(var.roles)}"
name = "${element(restapi_object.cloudx_hostname.*.id, count.index + 1)}"
configure_for_dns = true
view = "Internal"
ipv4addr {
address = "func:nextavailableip:${var.ip_cidr}"
}
}
as a temporary workaround, you can ignore a recreation by adding
lifecycle {
ignore_changes = ["ipv4addr"]
}
@on4tux Try #51 ;)
Sorry guys for the delayed push upstream, I had this long ready. Had a lot to do in recent weeks.
Would it be possible to have an example of this in use with the vsphere provider. I would like to terraform a new vm (or multiple vms), select the next available IP from the range and pass it to the network interface (ipv4_address) for the new vm. When i attempt this, terraform is continuing to see the same next available address for all of the instances being created if more than 1. If i create a single instance, and then rerun, it will build a second instance with a new (next) IP correctly. Is this because terraform sees the IP as still free during the initial apply, even if you are building multiple vms? I'm still very new to terraform and assume I'm just doing this all wrong.
ie:
resource "infoblox_ip" "my_ip" { count = "${var.countnum}" ip_range = "192.168.100.10-192.168.100.20" }
resource "vsphere_virtual_machine" "vm" { count = "${var.countnum}" name = "${var.0-AppName}-${var.0-AppEnv}-${count.index + 1}"
resource_pool_id = "${data.vsphere_resource_pool.pool.id}" datastore_id = "${data.vsphere_datastore.datastore.id}" num_cpus = 2 memory = 2048 guest_id = "${data.vsphere_virtual_machine.template.guest_id}" scsi_type = "${data.vsphere_virtual_machine.template.scsi_type}" network_interface { network_id = "${data.vsphere_network.network.id}" adapter_type = "${data.vsphere_virtual_machine.template.network_interface_types[0]}" } disk { label = "disk0" size = "${data.vsphere_virtual_machine.template.disks.0.size}" eagerly_scrub = "${data.vsphere_virtual_machine.template.disks.0.eagerly_scrub}" thin_provisioned = "${data.vsphere_virtual_machine.template.disks.0.thin_provisioned}" } clone { template_uuid = "${data.vsphere_virtual_machine.template.id}" customize { linux_options { host_name = "${var.0-AppName}-${var.0-AppEnv}-${count.index + 1}" domain = "test.some.domain" } network_interface { ipv4_address = "${element(infoblox_ip.my_ip.*.ipaddress,count.index)}" ipv4_netmask = 24 dns_server_list = ["192.168.100.2"] } ipv4_gateway = "192.168.132.1" } } }
resource "infoblox_record_host" "my_host" { count = "${var.countnum}" name = "${var.0-AppName}-${var.0-AppEnv}-${count.index + 1}.test.some.domain" view = "Internal" configure_for_dns = true ipv4addr { address = "${element(infoblox_ip.my_ip..ipaddress, count.index)}" configure_for_dhcp = true mac ="${element(vsphere_virtual_machine.vm..network_interface.0.mac_address, count.index)}" }
You'll have to play with the func:nextavailableip function. I deleted quite some lines in my example below, but you should be able to grasp the idea
variable "ip_cidr" { default = "10.0.0.0/24" }
resource "infoblox_record_host" "host" { count = "${length(var.roles)}" name = "${lower(element(restapi_hostname.*.id, count.index))}.local" configure_for_dns = true view = "internal"
ipv4addr { address = "func:nextavailableip:${var.ip_cidr}" }
lifecycle { ignore_changes = ["ipv4addr"] } }
provider "vsphere" { user = "${var.username}" password = "${var.password}" vsphere_server = "localhost" }
resource "vsphere_virtual_machine" "vm" { count = "${length(var.roles)}" name = "${element(restapi_hostname.*.id, count.index)}"
network_interface { network_id = "${data.vsphere_network.network1.id}" adapter_type = "${data.vsphere_virtual_machine.template.network_interface_types[0]}" } }
thanks! i was able to get that working. do you have a working method of adding exclusions? it seems that the function for nextavailableip, doesn't like a range, just cidr, and even with a cider I can't give and exclude array.
The problem with the infobolx_ip resource is that it is rather a datasource than a resource. It only queries available IPs but does not make a reservation for them. So there is always a race condition between querying and creating a record for the IP. The terraform provider from infoblox infobloxopen/terraform-provider-infoblox supports creating fixedaddress and host objects using the following configuration:
provider "infoblox" {
server = "ipam.your-domain.com"
username = "admin"
password = "infoblox"
sslmode = false
}
resource "infoblox_ip_allocation" "nodeip" {
count = 10
network_view_name = "default"
cidr = "192.168.123.0/24"
vm_name="node${count.index}"
tenant_id="none"
}
Then you can use the IPs in other resources with infoblox_ip_allocation.nodeip.*.ip_addr[count.index]