Skip to content

Split Tailscale ACL (gitops-acl-action) from tf/tailscale (Terraform)

Date: 2026-07-05 Status: Accepted Decision: hl-swfp Deciders: Sean Brandt

The fzymgc-house tailnet’s ACL policy was already GitOps-managed via tailscale/gitops-acl-action in a standalone repo (fzymgc-house/tailscale-config), while DNS, tailnet settings, and OAuth clients were hand-managed in the admin console. Centralizing all tailnet config into the selfhosted-cluster monorepo forced a choice: unify everything under one Terraform resource (tailscale_acl), or keep the existing action for policy and add Terraform only for the non-ACL surface.

ACL content (acls, tagOwners, ssh, autoApprovers, hosts, groups, nodeAttrs) stays owned by tailscale/policy.hujson plus the relocated gitops-acl-action workflow (test on PR, apply on push to main, path-filtered to tailscale/**). A new tf/tailscale Terraform/TFC workspace owns everything non-ACL: DNS (tailscale_dns_configuration), tailnet settings (tailscale_tailnet_settings, including acls_externally_managed_on/acls_external_link), and OAuth clients (tailscale_oauth_client). The acls_externally_managed_on/acls_external_link fields are the sole cross-system coordination point.

  • tailscale_acl was explicitly evaluated and rejected to retain the action’s native sshTests execution against the live tailnet and HuJSON ergonomics.
  • acls_externally_managed_on/acls_external_link are tailnet-settings metadata, not ACL content, so Terraform owning them does not collide with the action pushing the policy via API.
  • Relocating (not rewriting) the existing, Renovate-maintained action is lower risk than porting the policy into a Terraform resource.
  • Keep ACL on gitops-acl-action + tf/tailscale for the rest (chosen): preserves sshTests + HuJSON tooling; low-risk relocation. Cost: two systems govern the tailnet, requiring one explicit coordination point.
  • Full ACL-in-Terraform via tailscale_acl (rejected): single IaC tool/state, but loses native sshTests execution and HuJSON ergonomics, and is a larger migration for no functional gain.
  • Positive: a clean, documented ownership boundary; the two systems never race to apply the same tailnet; the ACL keeps its native test gate.
  • Negative: contributors must know which of the two tools to touch for a given tailnet change.
  • Neutral: acls_externally_managed_on flips to true (console policy editor locked) — UI-only; the action still applies via API.