# Managing your network with Terraform

The Defined Networking provider lets you manage your Managed Nebula network as code with [Terraform](https://developer.hashicorp.com/terraform) or [OpenTofu](https://opentofu.org). 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](/docs/guides/enrolling-hosts-with-join-keys/) or the [API](/docs/guides/automating-host-creation/).

## Creating an API key

The provider authenticates with a Defined Networking API key. On the [API Keys](https://admin.defined.net/settings/api-keys) page of the admin panel, create a key with the `create`, `read`, `update`, and `delete` [scopes](/docs/api-key-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:delete`
- `tags: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:

```sh
export DEFINEDNET_API_KEY="dnkey-..."
```

## 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.

```hcl title="main.tf"
terraform {
  required_providers {
    definednet = {
      source = "definednet/definednet"
    }
  }
}

# Reads the API key from the DEFINEDNET_API_KEY environment variable.
provider "definednet" {}
```

Then download the provider:

**Tab: Terraform**

```sh
terraform init
```

**Tab: OpenTofu**

```sh
tofu init
```

## 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.

```hcl title="main.tf"
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`, or `port_range` to 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

Preview the changes, then apply them:

**Tab: Terraform**

```sh
terraform plan
terraform apply
```

**Tab: OpenTofu**

```sh
tofu plan
tofu apply
```

The 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

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:

```hcl title="main.tf"
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

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:

```hcl title="main.tf"
resource "definednet_role" "db" {
  name = "db"
}

resource "definednet_tag" "staging" {
  name = "env:staging"
}
```

Then import each one:

**Tab: Terraform**

```sh
terraform import definednet_role.db role-LO4SPDSWTZNJC676WFCZKUB3ZQ
terraform import definednet_tag.staging env:staging
```

**Tab: OpenTofu**

```sh
tofu import definednet_role.db role-LO4SPDSWTZNJC676WFCZKUB3ZQ
tofu import definednet_tag.staging env:staging
```

Run 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

Every resource and data source, with all of its arguments, is documented on the [Terraform Registry](https://registry.terraform.io/providers/definednet/definednet/latest/docs) and the [OpenTofu Registry](https://search.opentofu.org/provider/definednet/definednet/latest). The provider's source is on [GitHub](https://github.com/DefinedNet/terraform-provider-definednet).
