Docs / Terraform

Quick start with Terraform

No fuss: a working main.tf, five commands, and a real allocated subnet by the end of it. The provider is published on the public Terraform Registry as uk-sw/nxip. Working in IPv6? There's a dedicated IPv6 quick start.

Quick start

1. Get an API key. Sign up. Free tier is enough to run this end to end.

2. Set it as an environment variable. The provider reads it automatically, so no credential goes in a .tf file.

export NXIP_API_KEY="<your key>"

That only lasts for the current terminal session. To make it stick around:

  • Recommended: direnv. Put the same export line in a .envrc file in your project directory, add .envrc to .gitignore, then run direnv allow once. It loads automatically whenever you cd in, and the key stays scoped to this project rather than every shell you open.
  • Simpler: add the same line to your shell profile (~/.zshrc or ~/.bashrc). Works everywhere with no extra tool, at the cost of the key being available in every terminal, sitting in plaintext in a dotfile you might sync or share elsewhere.

Either way: never paste the key directly into a .tf file or commit it anywhere.

3. Save this as main.tf. One file, five resources: a pool, a subnet that finds its own home, that subnet split explicitly across two AZs, and one exact address reserved inside one of them.

terraform {
  required_providers {
    nxip = {
      source  = "uk-sw/nxip"
      version = "~> 0.2" # optional, but recommended
    }
  }
}

provider "nxip" {
  # api_key / url also read from NXIP_API_KEY / NXIP_URL env vars
}

resource "nxip_pool" "production_us_east" {
  name        = "Production US-East"
  cidr        = "10.100.0.0/16"
  family      = "IPV4"
  environment = "production"
  region      = "us-east-1"
}

# Auto-resolves onto the pool above by matching environment/region/family.
resource "nxip_subnet" "payments" {
  family        = nxip_pool.production_us_east.family
  environment   = nxip_pool.production_us_east.environment
  region        = nxip_pool.production_us_east.region
  prefix_length = 24
  name          = "Payments team"
}

# Explicit nesting via parent_subnet_id: splits the team's subnet across two AZs.
resource "nxip_subnet" "payments_az_a" {
  parent_subnet_id = nxip_subnet.payments.id
  family            = "IPV4"
  prefix_length     = 27
  kind              = "az-subnet"
  name              = "Payments AZ-a"
}

resource "nxip_subnet" "payments_az_b" {
  parent_subnet_id = nxip_subnet.payments.id
  family            = "IPV4"
  prefix_length     = 27
  kind              = "az-subnet"
  name              = "Payments AZ-b"
}

# One exact address inside that AZ subnet, e.g. a load balancer VIP.
resource "nxip_address" "payments_lb_vip" {
  subnet_id = nxip_subnet.payments_az_a.id
  address   = cidrhost(nxip_subnet.payments_az_a.cidr, 1)
  status    = "RESERVED"
  hostname  = "payments-lb-01"
}

output "payments_subnet_cidr" {
  value = nxip_subnet.payments.cidr
}

output "payments_az_a_cidr" {
  value = nxip_subnet.payments_az_a.cidr
}

output "payments_az_b_cidr" {
  value = nxip_subnet.payments_az_b.cidr
}

output "payments_lb_vip_address" {
  value = nxip_address.payments_lb_vip.address
}
  • production_us_east is the pool: the parent range everything else carves from.
  • payments finds its own home automatically. It only declares environment/region/family, and nxip resolves it onto the pool above with no hardcoded parent.
  • payments_az_a and payments_az_b nest explicitly under payments via parent_subnet_id, the deliberate path for splitting a team's range across AZs.
  • payments_lb_vip reserves one exact address inside payments_az_a, using cidrhost() to pick an address rather than hardcoding one.

4. Run it.

terraform init
terraform apply

What you'll see, trimmed to the parts that matter:

Plan: 5 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Enter a value: yes

nxip_pool.production_us_east: Creating...
nxip_pool.production_us_east: Creation complete after 0s [id=cm...]
nxip_subnet.payments: Creating...
nxip_subnet.payments: Creation complete after 0s [id=cm...]
nxip_subnet.payments_az_a: Creating...
nxip_subnet.payments_az_a: Creation complete after 0s [id=cm...]
nxip_subnet.payments_az_b: Creating...
nxip_subnet.payments_az_b: Creation complete after 0s [id=cm...]
nxip_address.payments_lb_vip: Creating...
nxip_address.payments_lb_vip: Creation complete after 0s [id=cm...]

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

Outputs:

payments_az_a_cidr = "10.100.0.0/27"
payments_az_b_cidr = "10.100.0.32/27"
payments_lb_vip_address = "10.100.0.1"
payments_subnet_cidr = "10.100.0.0/24"
Every attribute on these resources is immutable: changing one replaces the resource rather than editing it in place, since address blocks and reservations aren't safely mutable once something might already be using them.

Clean up when you're done

Free tier caps out at 6 pools and 20 subnets, so destroying what you don't need frees that quota back up for the next thing you try.

terraform destroy
Plan: 0 to add, 0 to change, 5 to destroy.

Do you really want to destroy all resources?
  Enter a value: yes

nxip_address.payments_lb_vip: Destroying...
nxip_address.payments_lb_vip: Destruction complete after 0s
nxip_subnet.payments_az_b: Destroying...
nxip_subnet.payments_az_b: Destruction complete after 0s
nxip_subnet.payments_az_a: Destroying...
nxip_subnet.payments_az_a: Destruction complete after 0s
nxip_subnet.payments: Destroying...
nxip_subnet.payments: Destruction complete after 0s
nxip_pool.production_us_east: Destroying...
nxip_pool.production_us_east: Destruction complete after 0s

Destroy complete! Resources: 5 destroyed.

Watch it happen

The whole loop above, played back start to finish.

main.tf · terraform

Full resource docs (every attribute, import support) are on the Registry's docs tab. For the concepts behind auto-resolution and nesting, see Pools, subnets & hierarchy, or the IPv6 quick start for the same walkthrough in IPv6.