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

The `location` argument of domains, zones and endpoints accepts `ru` or `nl`.

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"
  location  = "ru"
}

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
}

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

```

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"
  location       = "ru"
  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"
  location  = "ru"
}

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.
