Skip to content

About

Terraform module: terraform-infoblox-dns-records

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

Repository files navigation

🟦 Infoblox NIOS DNS Records Terraform Module

Aggregates all nine NIOS DNS record types — A, AAAA, PTR, CNAME, MX, TXT, SRV, NS, and Alias — behind one module, each an independently-optional for_each collection keyed by a stable caller string. Built for infoblox v2.x / WAPI v2.12.3.

Terraform infoblox module type resources


🧩 Overview

  • 🟦 Manages nine NIOS DNS record resources — infoblox_a_record, infoblox_aaaa_record, infoblox_ptr_record, infoblox_cname_record, infoblox_mx_record, infoblox_txt_record, infoblox_srv_record, infoblox_ns_record, infoblox_alias_record.
  • 🧩 Aggregation module — there is no this keystone. Every record type is a named for_each collection (a_records, aaaa_records, …) over its own map(object(...)), keyed by a caller-supplied stable string.
  • 🎚️ Each collection is independently optional (defaults to {}). Populate only the record types you need; the rest create nothing.
  • 🌐 Module-level shared defaults — one dns_view and one default_ttl apply to every record, with per-record overrides inside each object.
  • 🏷️ Exposes the universal NIOS fields comment and ext_attrs (plain map(string), jsonencoded internally) on every record type except ns_records, whose WAPI object has none.
  • 🔗 Emits one { key => WAPI ref } map per record type (the aggregation analogue of a single id), plus a merged record_fqdns map for monitoring and DTC wiring.

💡 Why it matters: DNS records share an identical shape (fqdn, dns_view, comment, ext_attrs, ttl) and are almost always provisioned together against the same zone. One aggregation module with nine optional collections is the right boundary — far cleaner than nine separate module calls.


❤️ Support this project

If these Terraform modules have been helpful to you or your organization, I'd appreciate your support in any of the following ways:

Whether it's a star, a professional connection, or a coffee, every gesture helps keep these modules actively maintained and continually improving. Thank you for being part of the community!


🗺️ Where this fits in the family

flowchart TD
 NV["terraform-infoblox-network-view<br/>(network_view)"]
 DV["terraform-infoblox-dns-view<br/>(dns_view)"]
 ZA["terraform-infoblox-zone-auth<br/>(authoritative zone)"]
 DREC["terraform-infoblox-dns-records<br/>(9 record types)"]
 DTC["terraform-infoblox-dtc<br/>(LBDN / pool / server)"]
 IPAM["IPAM modules<br/>(network / range / allocation)"]

 NV -->|network_view| DV
 NV -->|network_view| IPAM
 DV -->|dns_view| ZA
 DV -->|dns_view| DREC
 ZA -->|zone must exist| DREC
 IPAM -.->|next_available_ip via cidr| DREC
 DREC -->|record_fqdns| DTC
 ZA -->|zone| DTC

 classDef this fill:#00B4D8,color:#fff,stroke:#0077A3,stroke-width:2px;
 class DREC this;
Loading

This module sits downstream of terraform-infoblox-dns-view and terraform-infoblox-zone-auth — the DNS view and the authoritative zone for every record's FQDN must already exist. Dynamic (cidr) A/AAAA/PTR records also depend on an IPAM network being present so NIOS can hand out the next-available address. The emitted record_fqdns map feeds DTC and monitoring.


🧬 What this module builds

flowchart TD
 subgraph MOD["terraform-infoblox-dns-records (aggregation — no this keystone)"]
 direction TB
 A["infoblox_a_record<br/>for_each a_records"]
 AAAA["infoblox_aaaa_record<br/>for_each aaaa_records"]
 PTR["infoblox_ptr_record<br/>for_each ptr_records"]
 CN["infoblox_cname_record<br/>for_each cname_records"]
 MX["infoblox_mx_record<br/>for_each mx_records"]
 TXT["infoblox_txt_record<br/>for_each txt_records"]
 SRV["infoblox_srv_record<br/>for_each srv_records"]
 NS["infoblox_ns_record<br/>for_each ns_records<br/>(no ttl/comment/ext_attrs)"]
 AL["infoblox_alias_record<br/>for_each alias_records"]
 end

 DV["dns_view (shared default)"] --> MOD
 TTL["default_ttl (shared default)"] --> MOD

 classDef rec fill:#00B4D8,color:#fff,stroke:#0077A3,stroke-width:1px;
 class A,AAAA,PTR,CN,MX,TXT,SRV,NS,AL rec;
Loading

Every box is a total for_each renderer over its collection. An empty collection ({}, the default) creates zero resources for that type — there is no overhead for record types you do not use.


✅ Provider / Versions

Requirement Version
Terraform >= 1.12.0
infobloxopen/infoblox >= 2.5.0, < 3.0 (GA v2.13.0 / WAPI v2.12.3)

The module declares only a required_providers block — no provider "infoblox" {} block. Grid connection details are configured at the root/pipeline level (see Quick Start).


🔑 Required NIOS Permissions

Role / License Required for Notes
DNS Admin role (or a scoped admin group with read-write on the target zones/views) Create / update / delete all nine record types Sufficient for the entire module — no superuser needed for record CRUD.
Cloud Network Automation license (or superuser ) The four required Extensible Attributes The license auto-provisions the EAs; without it they must be created manually.
superuser First-time auto-creation of the Terraform Internal ID EA Needed only if the EA is not already present in the grid.

⚠️ Zone & superuser callout. The authoritative zone for every record FQDN must exist before apply — the NIOS plugin cannot create DNS zones (use terraform-infoblox-zone-auth first). Record creation itself needs no superuser, but the Terraform Internal ID EA must exist; if your Terraform identity is a scoped DNS admin (not superuser), have a grid administrator pre-create all four EAs (below) before the first terraform apply, or apply fails with a WAPI/connection error.


📋 NIOS Prerequisites

These four Extensible Attributes must exist in the NIOS grid before terraform apply. They are added automatically by the Cloud Network Automation license, or created manually / via cURL:

# Extensible Attribute Type Notes
1 Tenant ID String Cloud tenancy identifier
2 CMP Type String Cloud Management Platform type (e.g. Terraform)
3 Cloud API Owned List (True, False) Marks objects managed via the Cloud API
4 Terraform Internal ID String, read-only (CR flag) Drift detection when a NIOS _ref changes outside Terraform

Create the Terraform Internal ID EA via WAPI (adjust grid host / WAPI version as needed):

curl -k -u "$INFOBLOX_USERNAME:$INFOBLOX_PASSWORD" \
 -H "Content-Type: application/json" \
 -X POST "https://${INFOBLOX_SERVER}/wapi/v2.12.3/extensibleattributedef" \
 -d '{
 "name": "Terraform Internal ID",
 "type": "STRING",
 "flags": "CR",
 "comment": "Internal ID for Terraform drift detection"
 }'
  • WAPI version: v2.12.3 (provider default; align with WAPI_VERSION).
  • The CR flags mark the EA as Cloud-API created and Read-only so operators do not edit it by hand.

📁 Module Structure

terraform-infoblox-dns-records/
├── providers.tf # required_providers only — NO provider {} block
├── variables.tf # dns_view, default_ttl + nine record collections
├── main.tf # nine for_each resources, no `this` keystone
├── outputs.tf # nine *_record_ids maps + merged record_fqdns
├── README.md # this file
└── SCOPE.md # in/out-of-scope resources, permissions, gotchas

⚙️ Quick Start

module "dns_records" {
  source = "git::https://github.com/microsoftexpert/terraform-infoblox-dns-records?ref=v1.0.0"

  dns_view = "default"

  a_records = {
    web01 = {
      fqdn    = "web01.corp.example.com"
      ip_addr = "10.20.30.10"
      comment = "Primary web node"
    }
  }

  cname_records = {
    www = {
      alias     = "www.corp.example.com"
      canonical = "web01.corp.example.com"
    }
  }
}

Wire the dns_view from the upstream DNS view module, and ensure the zone exists:

module "dns_view" {
  source = "git::https://github.com/microsoftexpert/terraform-infoblox-dns-view?ref=v1.0.0"
  name   = "corp-internal"
}

module "zone" {
  source   = "git::https://github.com/microsoftexpert/terraform-infoblox-zone-auth?ref=v1.0.0"
  fqdn     = "corp.example.com"
  dns_view = module.dns_view.dns_view
}

module "dns_records" {
  source   = "git::https://github.com/microsoftexpert/terraform-infoblox-dns-records?ref=v1.0.0"
  dns_view = module.dns_view.dns_view # ← wired here

  a_records = {
    web01 = { fqdn = "web01.corp.example.com", ip_addr = "10.20.30.10" }
  }

  depends_on = [module.zone] # zone must exist before records
}

Grid credentials (INFOBLOX_SERVER / INFOBLOX_USERNAME / INFOBLOX_PASSWORD, plus SSLMODE / WAPI_VERSION) are configured at the root / pipeline level — never inside this module.


🔌 Cross-Module Contract

Consumes

Input Type Source module
dns_view string terraform-infoblox-dns-view (dns_view output)
zone existence (implicit) terraform-infoblox-zone-auth — the zone in every fqdn must exist first
network_view + a network (implicit) IPAM modules — only for dynamic (cidr) A/AAAA/PTR allocation

Emits

All outputs are maps keyed by the caller's collection key — never null, empty {} when a collection is unused.

Output Description Consumed by
a_record_ids { key => WAPI ref } for A records (e.g. record:a/ZG5z...) cross-module wiring, monitoring
aaaa_record_ids { key => WAPI ref } for AAAA records wiring
ptr_record_ids { key => WAPI ref } for PTR records wiring
cname_record_ids { key => WAPI ref } for CNAME records wiring
mx_record_ids { key => WAPI ref } for MX records wiring
txt_record_ids { key => WAPI ref } for TXT records wiring
srv_record_ids { key => WAPI ref } for SRV records wiring
ns_record_ids { key => WAPI ref } for NS records wiring
alias_record_ids { key => WAPI ref } for Alias records wiring
record_fqdns merged { "type:key" => name } across all populated collections DTC, monitoring

The primary identifier convention (id) is realised here as the per-type *_record_ids maps — the aggregation analogue of a single id output. Each WAPI ref is the canonical identifier for terraform import and ID-based wiring.


📚 Example Library (copy-paste)

1 · Minimal — a single static A record
module "dns_records" {
  source = "git::https://github.com/microsoftexpert/terraform-infoblox-dns-records?ref=v1.0.0"

  a_records = {
    web01 = {
      fqdn    = "web01.corp.example.com"
      ip_addr = "10.20.30.10"
    }
  }
}
2 · A record with dynamic (next-available) allocation

Set cidr instead of ip_addr to let NIOS allocate the next-available IPv4 from a network. The two are mutually exclusive (validated).

module "dns_records" {
  source = "git::https://github.com/microsoftexpert/terraform-infoblox-dns-records?ref=v1.0.0"

  a_records = {
    app01 = {
      fqdn         = "app01.corp.example.com"
      cidr         = "10.20.30.0/24" # next_available_ip drawn from here
      network_view = "default"
      comment      = "Dynamically allocated app node"
    }
  }
}
3 · A record with extensible attributes (jsonencode pattern)

Callers pass a plain map(string); the module jsonencodes it internally and omits it entirely when empty (no "{}" drift).

module "dns_records" {
  source = "git::https://github.com/microsoftexpert/terraform-infoblox-dns-records?ref=v1.0.0"

  a_records = {
    web01 = {
      fqdn    = "web01.corp.example.com"
      ip_addr = "10.20.30.10"
      ext_attrs = {
        "Tenant ID"       = "corp"
        "CMP Type"        = "Terraform"
        "Cloud API Owned" = "True"
      }
    }
  }
}
4 · AAAA record (IPv6)
module "dns_records" {
  source = "git::https://github.com/microsoftexpert/terraform-infoblox-dns-records?ref=v1.0.0"

  aaaa_records = {
    web01_v6 = {
      fqdn      = "web01.corp.example.com"
      ipv6_addr = "2001:db8:abcd:12::10"
      ttl       = 3600
    }
  }
}
5 · PTR record — all three shapes

A PTR is either forward-mapping (record_name), reverse static (ip_addr), or reverse dynamic (cidr). Exactly one is set (validated).

module "dns_records" {
  source = "git::https://github.com/microsoftexpert/terraform-infoblox-dns-records?ref=v1.0.0"

  ptr_records = {
    rev_static = {
      ptrdname = "web01.corp.example.com" # reverse zone, static
      ip_addr  = "10.20.30.10"
    }
    rev_dynamic = {
      ptrdname     = "app01.corp.example.com" # reverse zone, dynamic
      cidr         = "10.20.30.0/24"
      network_view = "default"
    }
    fwd = {
      ptrdname    = "host.corp.example.com" # forward-mapping zone
      record_name = "alias.corp.example.com"
    }
  }
}
6 · CNAME record
module "dns_records" {
  source = "git::https://github.com/microsoftexpert/terraform-infoblox-dns-records?ref=v1.0.0"

  cname_records = {
    www = {
      alias     = "www.corp.example.com"
      canonical = "web01.corp.example.com"
      comment   = "Public alias for the primary web node"
    }
  }
}
7 · MX records (mail exchangers)
module "dns_records" {
  source = "git::https://github.com/microsoftexpert/terraform-infoblox-dns-records?ref=v1.0.0"

  mx_records = {
    primary = {
      fqdn           = "corp.example.com"
      mail_exchanger = "mail1.corp.example.com"
      preference     = 10
    }
    secondary = {
      fqdn           = "corp.example.com"
      mail_exchanger = "mail2.corp.example.com"
      preference     = 20
    }
  }
}
8 · TXT record (SPF)

TXT data is arbitrary and not case-folded. Quote the value to preserve spaces.

module "dns_records" {
  source = "git::https://github.com/microsoftexpert/terraform-infoblox-dns-records?ref=v1.0.0"

  txt_records = {
    spf = {
      fqdn = "corp.example.com"
      text = "\"v=spf1 include:_spf.example.com -all\""
    }
  }
}
9 · SRV record (service location)
module "dns_records" {
  source = "git::https://github.com/microsoftexpert/terraform-infoblox-dns-records?ref=v1.0.0"

  srv_records = {
    sip = {
      name     = "_sip._tcp.corp.example.com"
      target   = "sipserver.corp.example.com"
      port     = 5060
      priority = 10
      weight   = 50
    }
  }
}
10 · NS record (delegation glue) — no ttl/comment/ext_attrs

The NIOS NS-record object has no ttl, comment, or ext_attrs — those keys are absent from the schema. At least one glue address is required.

module "dns_records" {
  source = "git::https://github.com/microsoftexpert/terraform-infoblox-dns-records?ref=v1.0.0"

  ns_records = {
    sub = {
      name       = "sub.corp.example.com"
      nameserver = "ns1.corp.example.com"
      addresses = [
        { address = "10.20.30.5", auto_create_ptr = true },
        { address = "10.20.30.6" },
      ]
    }
  }
}
11 · Alias record (record:alias)
module "dns_records" {
  source = "git::https://github.com/microsoftexpert/terraform-infoblox-dns-records?ref=v1.0.0"

  alias_records = {
    legacy = {
      name        = "legacy.corp.example.com"
      target_name = "web01.corp.example.com"
      target_type = "A" # one of: A, AAAA, MX, NAPTR, PTR, SPF, SRV, TXT
    }
  }
}
12 · Module-level default_ttl with per-record override

default_ttl applies to every record that does not set its own ttl. A per-record ttl (including 0) always wins.

module "dns_records" {
  source = "git::https://github.com/microsoftexpert/terraform-infoblox-dns-records?ref=v1.0.0"

  default_ttl = 3600 # applies to all records below…

  a_records = {
    web01  = { fqdn = "web01.corp.example.com", ip_addr = "10.20.30.10" }             # inherits 3600
    cache0 = { fqdn = "cache.corp.example.com", ip_addr = "10.20.30.11", ttl = 0 }    # disables caching
    edge0  = { fqdn = "edge.corp.example.com", ip_addr = "10.20.30.12", ttl = 86400 } # explicit override
  }
}
13 · Per-record dns_view override

The module-level dns_view is the default; any record can override it.

module "dns_records" {
  source   = "git::https://github.com/microsoftexpert/terraform-infoblox-dns-records?ref=v1.0.0"
  dns_view = "corp-internal"

  a_records = {
    internal = { fqdn = "app.corp.example.com", ip_addr = "10.0.0.5" }                      # corp-internal
    dmz      = { fqdn = "app.corp.example.com", ip_addr = "203.0.113.5", dns_view = "dmz" } # override
  }
}
14 · Consume the output maps
output "web01_ref" {
  value = module.dns_records.a_record_ids["web01"] # e.g. record:a/ZG5z...:web01.corp.example.com/default
}

output "all_managed_names" {
  value = module.dns_records.record_fqdns # { "a:web01" = "web01.corp.example.com", "cname:www" = "www...",... }
}
15 · End-to-end composition (dns_view → zone → many record types)
module "dns_view" {
  source = "git::https://github.com/microsoftexpert/terraform-infoblox-dns-view?ref=v1.0.0"
  name   = "corp-internal"
}

module "zone" {
  source   = "git::https://github.com/microsoftexpert/terraform-infoblox-zone-auth?ref=v1.0.0"
  fqdn     = "corp.example.com"
  dns_view = module.dns_view.dns_view
}

module "dns_records" {
  source     = "git::https://github.com/microsoftexpert/terraform-infoblox-dns-records?ref=v1.0.0"
  dns_view   = module.dns_view.dns_view
  depends_on = [module.zone]

  default_ttl = 3600

  a_records = {
    web01 = { fqdn = "web01.corp.example.com", ip_addr = "10.20.30.10" }
    web02 = { fqdn = "web02.corp.example.com", ip_addr = "10.20.30.11" }
  }

  cname_records = {
    www = { alias = "www.corp.example.com", canonical = "web01.corp.example.com" }
  }

  mx_records = {
    primary = { fqdn = "corp.example.com", mail_exchanger = "mail1.corp.example.com", preference = 10 }
  }

  txt_records = {
    spf = { fqdn = "corp.example.com", text = "\"v=spf1 include:_spf.example.com -all\"" }
  }

  ptr_records = {
    web01 = { ptrdname = "web01.corp.example.com", ip_addr = "10.20.30.10" }
  }
}

📥 Inputs

Name Type Default Required Description
dns_view string "default" — Module-level DNS view for every record; per-record dns_view overrides it.
default_ttl number null — Module-level TTL for records that don't set their own ttl. null = inherit from zone; 0 = no caching. Does not apply to ns_records.
a_records map(object) {} — A records. Per record: fqdn, exactly one of ip_addr/cidr, optional network_view, filter_params, dns_view, ttl, comment, ext_attrs.
aaaa_records map(object) {} — AAAA records. As A records but ipv6_addr instead of ip_addr.
ptr_records map(object) {} — PTR records. Per record: ptrdname + exactly one of record_name/ip_addr/cidr.
cname_records map(object) {} — CNAME records. Per record: alias, canonical.
mx_records map(object) {} — MX records. Per record: fqdn, mail_exchanger, preference (0–65535).
txt_records map(object) {} — TXT records. Per record: fqdn, text (arbitrary, not case-folded).
srv_records map(object) {} — SRV records. Per record: name, target, port, priority, weight (each 0–65535).
ns_records map(object) {} — NS records. Per record: name, nameserver, non-empty addresses list, optional dns_view. No ttl/comment/ext_attrs.
alias_records map(object) {} — Alias records. Per record: name, target_name, target_type (A/AAAA/MX/NAPTR/PTR/SPF/SRV/TXT), optional disable.

Every name/FQDN-bearing field is validated lowercase (NIOS DNS is case-sensitive). See each variable's heredoc description in variables.tf for the full object schema.


🧾 Outputs

Output Description Typically consumed by
a_record_ids Primary (A). { key => WAPI ref }, e.g. record:a/.... {} when unused. wiring, terraform import, monitoring
aaaa_record_ids { key => WAPI ref } (record:aaaa/...). wiring
ptr_record_ids { key => WAPI ref } (record:ptr/...). wiring
cname_record_ids { key => WAPI ref } (record:cname/...). wiring
mx_record_ids { key => WAPI ref } (record:mx/...). wiring
txt_record_ids { key => WAPI ref } (record:txt/...). wiring
srv_record_ids { key => WAPI ref } (record:srv/...). wiring
ns_record_ids { key => WAPI ref } (record:ns/...). wiring
alias_record_ids { key => WAPI ref } (record:alias/...). wiring
record_fqdns merged { "type:key" => name } across all collections. DTC, monitoring, reporting

🧠 Architecture Notes

WAPI reference string (the id convention). Each record's id is the NIOS object reference, e.g. record:a/ZG5zLnpvbmUkLl9kZWZhdWx0...:web01.corp.example.com/default. It is opaque, grid-specific, and is the canonical identifier for terraform import and reference-by-ID wiring. As an aggregation module there is no single id; the contract is realised as one { key => _ref } map per record type (a_record_ids, …) plus the merged record_fqdns.

NIOS object hierarchy. network_view → dns_view → zone (zone_auth) → record. This module owns only the leaf records. The DNS view (dns_view, default "default") and the authoritative zone for every FQDN must already exist — the plugin cannot create zones. Provision terraform-infoblox-dns-view and terraform-infoblox-zone-auth upstream.

Immutable fields (force-new). Renaming a record's identity (fqdn / alias / name / ptrdname) is a destroy-and-recreate. So is changing the map key — keys are the Terraform identity for each record and are immutable-by-convention; changing a key destroys and recreates that record. Switching a record between static (ip_addr/ipv6_addr) and dynamic (cidr) allocation also recreates it.

ext_attrs encoding. Callers pass a plain map(string). main.tf encodes per record:

ext_attrs = length(try(each.value.ext_attrs, {})) > 0 ? jsonencode(each.value.ext_attrs) : null

The length(...) > 0 guard is required because the provider expects a JSON string, not a map. Encoding an empty map would render "{}", which the provider then reports as perpetual drift on every plan; encoding to null omits the argument entirely so unused EAs stay quiet. ns_records has no ext_attrs — the NIOS NS-record object exposes none.

TTL behavior. Resolution per record is: per-record ttl wins, else module-level default_ttl. Then null (both unset) → inherit from the parent zone (NIOS stores this as a negative value in state, by design — not drift); 0 → disable caching for the record; positive integer → explicit TTL in seconds. A per-record ttl = 0 is honoured because 0 != null. ns_records has no ttl — the NS-record object has no TTL field.

Terraform Internal ID EA (drift detection). When present (auto-created under a superuser identity, or pre-created), the provider stamps this EA onto every managed record so it can re-locate the object if the NIOS _ref changes due to out-of-band manual edits — preventing spurious destroy/recreate churn. Pre-create it if your identity is not superuser.

Case sensitivity — lowercase FQDNs only. NIOS DNS lookups are case-sensitive; capital letters cause silent lookup failures. Every name/FQDN-bearing field across all nine collections is validated lowercase (fqdn, alias, canonical, ptrdname, record_name, mail_exchanger, SRV name/target, NS name/nameserver, alias name/target_name). TXT text is the one exception — TXT data is arbitrary and not case-folded.

Eventual consistency. NIOS is a distributed grid with read-after-write lag; a freshly created record may not be immediately queryable. Allow brief propagation before dependent reads in tightly chained pipelines.


🧱 Design Principles

  • Aggregation, not nine modules — DNS records share an identical shape and are provisioned together; one module with nine optional collections is the right boundary.
  • No this keystone — every resource is named by role (a_records, cname_records, …) and driven by for_each. No count.
  • Each collection independently optional — map(object(...)) default {}, so callers populate only what they need.
  • Shared defaults + per-record overrides — module-level dns_view/default_ttl avoid repetition while preserving per-record control.
  • Map-of-maps outputs keyed by the caller's key — deterministic wiring; an unused collection yields {}, never an error.
  • ext_attrs is the NIOS equivalent of tags — map(string), jsonencoded, guarded against "{}" drift. No Azure tags/resource_group_name/object_id.
  • NS-record exception surfaced — ns_records omits ttl/comment/ext_attrs because the WAPI object has none.
  • Host records excluded — infoblox_ip_allocation (host record) is a distinct family owned by terraform-infoblox-ip-allocation.

🚀 Runbook

terraform init -backend=false
terraform validate
terraform fmt -check

terraform plan / apply require a live NIOS grid endpoint and the auth env vars (INFOBLOX_SERVER / INFOBLOX_USERNAME / INFOBLOX_PASSWORD). init -backend=false + validate + fmt -check run fully offline.


🧪 Testing

  • Offline: terraform init -backend=false && terraform validate && terraform fmt -check — confirms schema, type, and validation-block correctness without a grid.
  • Validation coverage to exercise: lowercase enforcement on every FQDN field; A/AAAA ip_addr vs cidr mutual exclusion; PTR exactly-one-of record_name/ip_addr/cidr; MX preference 0–65535; SRV port/priority/weight ranges; alias target_type enum; NS non-empty addresses.
  • Integration (against a lab grid): stand up dns_view + zone_auth, then apply a small mix of record types and assert the *_record_ids maps are populated and record_fqdns is correctly namespaced.
  • Idempotence: a second plan after apply must report no changes — in particular no ext_attrs "{}" drift and no TTL-inherit (negative value) churn.

💬 Example Output

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.

Outputs:

a_record_ids = {
 "web01" = "record:a/ZG5zLmJpbmRfYSQuX2RlZmF1bHQ...:web01.corp.example.com/default"
}
cname_record_ids = {
 "www" = "record:cname/ZG5zLmJpbmRfY25hbWUk...:www.corp.example.com/default"
}
mx_record_ids = {}
record_fqdns = {
 "a:web01" = "web01.corp.example.com"
 "cname:www" = "www.corp.example.com"
}

🔍 Troubleshooting

Symptom Likely cause Fix
Apply fails immediately with a connection / WAPI error One of the four required EAs (esp. Terraform Internal ID) is missing Pre-create the EAs (cURL above) or run with the Cloud Network Automation license / superuser.
Unknown extensible attribute "..." on apply An EA key in a record's ext_attrs is not defined in the grid Define the EA in NIOS first (see Prerequisites), or remove the key.
... zone not found / record fails to create The authoritative zone for the FQDN doesn't exist Create it via terraform-infoblox-zone-auth first; add depends_on on the zone module.
Silent lookup failure / record not resolving An FQDN had uppercase letters and NIOS case-folded inconsistently Use lowercase only — the module validates this; fix the input.
401 Unauthorized / auth failure INFOBLOX_USERNAME / PASSWORD / SERVER not set at root Export the env vars (or set them in the pipeline) before plan/apply.
Plan wants to destroy and recreate a record A map key, fqdn/name/alias, or static↔dynamic mode was changed (force-new) Revert the change, or accept the recreate; treat map keys as immutable.
Every plan shows ext_attrs drift ("{}") An empty map was pushed without the length > 0 guard This module already guards it; confirm you're on v1.0.0+.
Plan shows TTL "drift" to a negative number ttl/default_ttl is null → NIOS stores the inherited TTL as a negative value Expected behavior, not drift. Set an explicit ttl if you want a fixed value.
Terraform Internal ID import / re-create churn The _ref changed out-of-band and the EA is absent Ensure the Terraform Internal ID EA exists so the provider can re-locate objects.
Record created but not immediately queryable NIOS eventual consistency (read-after-write lag) Allow brief propagation; add depends_on for tightly chained reads.
... must set exactly one of... validation error A/AAAA set both/neither ip_addr/cidr, or PTR set ≠1 of the three shapes Set exactly one allocation field per the variable description.

🔗 Related Docs

  • Provider: infobloxopen/infoblox — infoblox_*_record resources
  • Infoblox NIOS documentation
  • SCOPE.md (this module) — in/out-of-scope resources, permissions, prerequisites, gotchas, design decisions
  • Upstream: terraform-infoblox-dns-view, terraform-infoblox-zone-auth
  • Downstream: terraform-infoblox-dtc (consumes record_fqdns)

💙 "Infrastructure as Code should be standardized, consistent, and secure."

About

Terraform module: terraform-infoblox-dns-records

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages