Managing your network with Terraform
The Defined Networking provider lets you manage your Managed Nebula network as code with Terraform or OpenTofu. Roles, tags, routes, and networks live in configuration files you can review and version, and a plan shows every change before it reaches your network.
The provider manages the parts of your network that change through review: networks, roles and their firewall rules, tags and their config overrides, routes, verified domains, and endpoint OIDC providers. It does not create or enroll hosts. To bring machines into the network automatically, use a join key or the API.
Creating an API key
Section titled “Creating an API key”The provider authenticates with a Defined Networking API key. On the API Keys page of the admin panel, create a key with the create, read, update, and delete scopes for each type of object you will manage. Data sources that look up a role, network, or route by name also need that type’s list scope. The example in this guide manages roles and tags, so it needs:
roles:create,roles:read,roles:update,roles:deletetags:create,tags:read,tags:update,tags:delete
The key is shown only once, so store it somewhere safe. The provider reads it from the DEFINEDNET_API_KEY environment variable:
export DEFINEDNET_API_KEY="dnkey-..."Configuring the provider
Section titled “Configuring the provider”Create a main.tf file that requires the provider. The same source address works with both Terraform and OpenTofu, which download the provider from their own registries.
terraform { required_providers { definednet = { source = "definednet/definednet" } }}
# Reads the API key from the DEFINEDNET_API_KEY environment variable.provider "definednet" {}Then download the provider:
terraform inittofu initDefining roles and tags
Section titled “Defining roles and tags”Add resources for the roles and tags you want. This example creates an admin role for operators, a web role whose hosts accept HTTPS from anyone and SSH only from admins, and an env:prod tag that turns down log verbosity on production hosts.
resource "definednet_role" "admin" { name = "admin" description = "Operators who can SSH into any host"}
resource "definednet_role" "web" { name = "web" description = "Public web servers"
firewall_rules = [ { protocol = "TCP" description = "HTTPS from anyone" port_range = { from = 443, to = 443 } }, { protocol = "TCP" description = "SSH from admins" allowed_role_id = definednet_role.admin.id port_range = { from = 22, to = 22 } }, { protocol = "ICMP" }, ]}
resource "definednet_tag" "prod" { name = "env:prod" description = "Production hosts"
config_overrides = [ { key = "logging.level", value = jsonencode("warning") }, ]}A few things to know when writing firewall rules and overrides:
- Firewall rules are a set, so their order in the file doesn’t matter. Leave out
allowed_role_id,allowed_tags, orport_rangeto allow every role, tag, or port. - To allow traffic between hosts that share a role, set
allowed_role_id = "self", since a resource can’t reference its own ID. - Config override values are JSON, so wrap every value in
jsonencode().
Applying changes
Section titled “Applying changes”Preview the changes, then apply them:
terraform planterraform applytofu plantofu applyThe new roles and tag appear in the admin panel right away, and hosts pick up changes the same way they do for edits made in the admin panel. From here, change your network by editing the configuration and applying it again.
A role or tag’s name can’t change after it’s created. Changing it makes Terraform delete the object and create a new one, which fails while hosts are still assigned to it.
Edits made in the admin panel to objects Terraform manages show up in the next plan as changes to undo, so make changes to those objects in the configuration instead.
Removing objects from Terraform
Section titled “Removing objects from Terraform”Deleting a resource block from the configuration deletes that object from your network on the next apply. Deleting a role or tag fails while hosts are still assigned to it.
To stop managing an object with Terraform but keep it in your network, replace its resource block with a removed block (Terraform 1.7 or OpenTofu 1.10 and later), then apply:
removed { from = definednet_role.web
lifecycle { destroy = false }}The object stays in the admin panel, and you can edit it there again.
Importing existing roles and tags
Section titled “Importing existing roles and tags”To bring roles and tags you created in the admin panel under Terraform, write a resource for each, then import it. Roles are imported by ID, which is shown at the top of the role’s page in the admin panel. Tags are imported by name.
For example, to import a db role and an env:staging tag, add resources that match them:
resource "definednet_role" "db" { name = "db"}
resource "definednet_tag" "staging" { name = "env:staging"}Then import each one:
terraform import definednet_role.db role-LO4SPDSWTZNJC676WFCZKUB3ZQterraform import definednet_tag.staging env:stagingtofu import definednet_role.db role-LO4SPDSWTZNJC676WFCZKUB3ZQtofu import definednet_tag.staging env:stagingRun a plan after importing. If it shows changes, update your configuration until the plan is empty, so the first apply doesn’t overwrite what’s already in place.
Reference
Section titled “Reference”Every resource and data source, with all of its arguments, is documented on the Terraform Registry and the OpenTofu Registry. The provider’s source is on GitHub.