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 reference for all resources and attributes is on the yuccastream/tuna 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.
terraform {
required_version = ">= 1.11"
required_providers {
tuna = {
source = "yuccastream/tuna"
}
}
}
provider "tuna" {}
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 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:
export TUNA_API_KEY="<API key>"
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: a subdomain of a built-in zone, of your own zone, or a custom domain |
tuna_domain_zone | Domain zone |
tuna_tls_certificate | TLS certificate for a domain zone |
tuna_gateway | Gateway on a reserved domain |
tuna_endpoint | TCP port |
tuna_ip_policy | IP policy |
tuna_ip_policy_rule | IP policy rule |
tuna_public_key | Public SSH key |
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.
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.
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:
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:
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
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
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. 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:
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>:
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.