# Terraform provider

The `yuccastream/tuna` provider manages tuna resources as code: domains and domain zones, gateways, endpoints, IP policies, TLS certificates, SSH keys and temporal tokens. It works with Terraform and OpenTofu.

> **The provider is in the Terraform Registry.** The reference for all resources and attributes is on the [yuccastream/tuna](https://registry.terraform.io/providers/yuccastream/tuna/latest/docs) page in the Terraform Registry.

## Installation

Terraform 1.11 or newer is required: older versions do not support write-only attributes, which are used to pass the TLS certificate private key.

```hcl
terraform {
  required_version = ">= 1.11"

  required_providers {
    tuna = {
      source = "yuccastream/tuna"
    }
  }
}

provider "tuna" {}

```

```bash
terraform init

```

The provider is not in the OpenTofu Registry yet. With OpenTofu, set the registry explicitly: `source = "registry.terraform.io/yuccastream/tuna"`.

## Authentication

The provider needs an API key. Issue one in the [dashboard](https://my.tuna.am/api_keys) with the `tunnels:write` scope: it covers every resource of the provider, full `api` access is not needed. A key with the default `read_api` scope is read-only, `apply` will fail with it.

API keys cannot be issued through the API, so the provider does not manage them: create keys in the dashboard only.

Pass the key via an environment variable (recommended) or in the `provider` block:

```bash
export TUNA_API_KEY="<API key>"

```

```hcl
provider "tuna" {
  api_key = var.tuna_api_key
}

```

| Argument  | Environment variable | Description                                   |
| --------- | -------------------- | --------------------------------------------- |
| `api_key` | `TUNA_API_KEY`       | API key                                       |
| `api_url` | `TUNA_API_URL`       | API address, `https://api.tuna.am` by default |

## Resources

| Resource               | Manages                                                                                                                          |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `tuna_domain`          | [Reserved domain](https://tuna.am/en/docs/team/domains.md): a subdomain of a built-in zone, of your own zone, or a custom domain |
| `tuna_domain_zone`     | [Domain zone](https://tuna.am/en/docs/team/domain_zones.md)                                                                      |
| `tuna_tls_certificate` | [TLS certificate](https://tuna.am/en/docs/team/tls_certificates.md) for a domain zone                                            |
| `tuna_gateway`         | [Gateway](https://tuna.am/en/docs/gateways.md) on a reserved domain                                                              |
| `tuna_endpoint`        | [TCP port](https://tuna.am/en/docs/team/tcp_ports.md)                                                                            |
| `tuna_ip_policy`       | [IP policy](https://tuna.am/en/docs/team/ip_policies.md)                                                                         |
| `tuna_ip_policy_rule`  | IP policy rule                                                                                                                   |
| `tuna_public_key`      | [Public SSH key](https://tuna.am/en/docs/team/ssh_public_keys.md)                                                                |
| `tuna_temporal_token`  | Temporal token for running tunnels                                                                                               |

The `tuna_tunnels` data source returns the tunnels of the account.

New domains and endpoints are created in a [cell](https://tuna.am/en/blog/high-availability.md), and location doesn't affect them, so starting with provider version 0.2.0, `tuna_domain` and `tuna_endpoint` no longer have a `location` argument. It remains only on `tuna_domain_zone`: it's optional, accepts `ru` or `nl`, and defaults to `ru`. If your configurations set `location` on domains or endpoints, remove it before upgrading the provider.

The full attribute reference is in the [Terraform Registry](https://registry.terraform.io/providers/yuccastream/tuna/latest/docs).

## Scenario: temporal tunnels for CI

Terraform reserves a domain and issues a limited temporal token, and a CI job starts a tunnel with that token. The main API key never reaches CI.

```hcl
resource "tuna_domain" "preview" {
  subdomain = "myapp-preview"
}

resource "tuna_temporal_token" "ci" {
  description              = "CI preview"
  duration                 = "24h"
  allow_http               = true
  allow_tcp                = false
  limit_by_active_tunnels  = 5
  limit_by_created_tunnels = 100
  domains                  = [tuna_domain.preview.domain]
}

output "ci_tunnel_token" {
  value     = tuna_temporal_token.ci.token
  sensitive = true
}

```

The `domains` argument binds the token to reserved domains, and `endpoints` binds it to [TCP ports](https://tuna.am/en/docs/team/tcp_ports.md) by alias, up to 100 addresses each. With a bound token, a tunnel starts only on these addresses, and the server rejects a tunnel without an address or an [App](https://tuna.am/en/docs/apps.md). [`tuna ssh`](https://tuna.am/en/docs/tunnels/ssh.md) with such a token gets a one-time host key instead of the member's shared key. `domains` requires `allow_http = true`, and `endpoints` requires `allow_tcp = true`. If the CI token leaks, nobody can use it to bring up a tunnel on another team domain.

The token is marked `sensitive`, so read it explicitly:

```bash
terraform apply
export TUNA_TOKEN="$(terraform output -raw ci_tunnel_token)"
tuna http 8080 --subdomain=myapp-preview

```

Check that the tunnel is up with the data source:

```hcl
data "tuna_tunnels" "all" {}

output "active_tunnels" {
  value = [for t in data.tuna_tunnels.all.tunnels : t.public_url if t.active]
}

```

## Scenario: own zone with a certificate and IP access

```hcl
resource "tuna_tls_certificate" "wildcard" {
  description     = "wildcard for tunnels.example.com"
  certificate_pem = file("${path.module}/cert.pem")
  private_key_pem = file("${path.module}/key.pem")
}

resource "tuna_domain_zone" "example" {
  domain             = "tunnels.example.com"
  location           = "ru"
  tls_certificate_id = tuna_tls_certificate.wildcard.id
}

resource "tuna_domain" "app" {
  subdomain      = "app"
  domain_zone_id = tuna_domain_zone.example.id
}

resource "tuna_ip_policy" "office" {
  name   = "office-only"
  active = true
}

resource "tuna_ip_policy_rule" "office_allow" {
  ip_policy_id = tuna_ip_policy.office.id
  name         = "office"
  cidr         = "203.0.113.0/24"
  action       = "allow"
}

```

`private_key_pem` is a write-only attribute: the provider sends the key to the API but does not store it in state.

## Gateway

```hcl
resource "tuna_domain" "api" {
  subdomain = "myapp-api"
}

resource "tuna_gateway" "api" {
  domain_id = tuna_domain.api.id
  policy    = file("${path.module}/policy.yaml")
}

```

`policy.yaml` holds a [traffic policy](https://tuna.am/en/docs/traffic-policy.md). A domain with a gateway is taken by the gateway: a tunnel on it will not start, the server responds with `Domain already reserved by gateway`. Reserve a separate domain for the tunnel.

## Changing resources

The API cannot edit endpoints, domains, domain zones and temporal tokens, so any change of their attributes (including `alias` or `description`) recreates the resource. A temporal token also gets a new value: tunnels started with the old token have to be restarted.

Gateways, IP policies and their rules are updated in place. A rule is recreated only when `ip_policy_id` changes.

## Import

Existing resources are imported by their numeric ID from the dashboard or the API:

```bash
terraform import tuna_domain.app 123
terraform import tuna_gateway.api 123

```

An IP policy rule has a composite ID `<ip_policy_id>/<rule_id>`:

```bash
terraform import tuna_ip_policy_rule.office_allow 123/456

```

The API does not return the certificate private key, so after importing `tuna_tls_certificate` set `private_key_pem` in the configuration.
