Import a live UniFi / Ubiquiti UDM controller into OpenTofu (Terraform) — enumerate the controller and generate clean, appliable HCL for the ubiquiti-community/unifi provider. Plan-only, re-runnable.
Project description
ubitofu — import your UniFi / Ubiquiti UDM config into OpenTofu (Terraform)
ubitofu enumerates a live UniFi Network controller (UDM, UDM-Pro, Cloud Key, or
self-hosted) and generates clean, directly-appliable OpenTofu/Terraform HCL for the
ubiquiti-community/unifi provider — bringing existing networks, VLANs, WLANs,
firewall rules, port profiles, port forwards, WireGuard VPN, clients and devices under
infrastructure-as-code. Plan-only and re-runnable: run once to import an existing
controller, and again to reconcile drift.
It never runs tofu apply and never writes to the controller. Every run reads from the
controller and writes HCL to your working directory, so running it against production
networks is safe.
Installation
pip install ubitofu
Requires Python 3.11 or later. Runtime dependencies (python-hcl2, httpx, deepdiff) install automatically.
Both entry points are equivalent:
ubitofu --help
python -m ubitofu --help
Importing an existing UniFi controller into Terraform/OpenTofu
Four subcommands take you from a live controller to appliable code:
$ ubitofu enumerate --config config.toml # import blocks + coverage gaps
$ ubitofu generate --config config.toml # imports.tf + generated.tf + unifi-variables.tf
$ ubitofu reconcile --config config.toml # merge drift into committed HCL in place
$ ubitofu verify --config config.toml # plan must be clean (or secrets-only)
enumeratewalks the controller and printsimportblocks plus a report of anything it cannot bring under management.generatewritesimports.tf,generated.tf, andunifi-variables.tf— a self-contained, appliable configuration for theubiquiti-community/unifiprovider.reconcileedits your committed, hand-tuned.tfin place, preserving comments and layout: it updates drifted scalars and names each changed nested attribute precisely in the report; appends newly-adopted objects with theirimportblocks (and, for objects that carry a secret, the matchingvariabledeclaration plus a warning to set it); flags resources that would be destroyed on apply; and distinguishes objects deleted on the controller from those configured but not yet applied. Re-runs against an unchanged controller are a no-op and never produce a duplicate resource name. The printed report is the product; nothing is applied.verifyruns a plan and passes only when it is clean (or the only diffs are in schema-sensitive attributes whose values live in variables).
Configuration is TOML:
controller_url = "https://192.168.1.1"
site = "default"
api_key_source = "op" # or "env"
api_key_ref = "op://YourVault/unifi.api-key/credential"
op_vault = "YourVault"
workdir = "./work"
Supported UniFi resources
ubitofu imports the resources the ubiquiti-community/unifi provider can manage:
networks and VLANs, WLANs, firewall rules and groups, port profiles, port forwards,
WireGuard VPN servers and peers, clients (by MAC), and devices.
ubitofu detects and reports resources the provider cannot manage — NAT rules, DNS content-filtering, device adoption, RF/firmware settings — never silently dropping them, so you always know what remains outside code.
Secrets
Secret attributes (WLAN passphrases, dynamic-DNS passwords, …) are never emitted as
plaintext. Each one known to the SECRETS table renders as a var.<name> reference,
and generate writes a unifi-variables.tf declaring every referenced variable
(type = string, sensitive = true) so the generated config is self-contained.
You supply variable values from your secret manager — e.g. TF_VAR_<name>
environment variables or a git-ignored *.auto.tfvars. The tool prints a suggested
secret-manager reference for each variable (rendered with your configured op_vault);
these references are reporter output only — the tool never writes them to files.
ubitofu omits sensitive attributes without a SECRETS rule from the HCL and adds them
to lifecycle { ignore_changes }. As a safety net, any emitted string value that still
looks secret-shaped (a secret-bearing attribute name, or a 44-char base64 WireGuard-key
shape) is suppressed the same way, with a loud warning naming the resource and
attribute — add a SECRETS rule to manage it properly.
How it compares
Unlike general-purpose importers such as terraformer, ubitofu is purpose-built for the
ubiquiti-community/unifi provider: it knows which attributes are settable, which are
computed, and which are secrets, so the HCL it emits applies cleanly instead of fighting
the provider schema. Compared with hand-rolled scripts (e.g. terrifi-style
one-offs), it is re-runnable and drift-aware — re-run verify any time to confirm code
and controller still agree.
Claude Code workflow skill
The repo ships a Claude Code skill at .claude/skills/unifi-tofu-reconcile-workflow/.
Its rule: never hand-author UniFi HCL — draft with ubitofu and refine.
If you run Claude Code inside a clone of this repo, the skill is available automatically.
pip install ubitofu does not install the skill — PyPI packages do not carry Claude Code
skills. Its best home is your own infrastructure repo: copy the
unifi-tofu-reconcile-workflow directory into that repo's .claude/skills/ and commit it,
so everyone who clones the repo gets it. To use it across every project instead, copy it
into ~/.claude/skills/.
License
Licensed under GPL-3.0-or-later — see LICENSE.
Also relevant if you searched: unifi terraform import, udm as code, opentofu ubiquiti, ubiquiti-community/unifi provider import.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ubitofu-0.3.1.tar.gz.
File metadata
- Download URL: ubitofu-0.3.1.tar.gz
- Upload date:
- Size: 71.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
82d60b1183c9e6a8aae2d24dcfbcb282cc3fef00b8dc2593d9b1104d8331ed50
|
|
| MD5 |
3958145bb7f8aaf3e5f8978c214bc0f6
|
|
| BLAKE2b-256 |
8c153e8a95a083e33187446cb0e685bd6bea83aa8c9cccebc757461891e87370
|
Provenance
The following attestation bundles were made for ubitofu-0.3.1.tar.gz:
Publisher:
release.yml on jamesbraid/ubitofu
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ubitofu-0.3.1.tar.gz -
Subject digest:
82d60b1183c9e6a8aae2d24dcfbcb282cc3fef00b8dc2593d9b1104d8331ed50 - Sigstore transparency entry: 2083566223
- Sigstore integration time:
-
Permalink:
jamesbraid/ubitofu@6608dcb9cd873fc1d4af5f367b706786a5dfdd60 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/jamesbraid
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6608dcb9cd873fc1d4af5f367b706786a5dfdd60 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ubitofu-0.3.1-py3-none-any.whl.
File metadata
- Download URL: ubitofu-0.3.1-py3-none-any.whl
- Upload date:
- Size: 48.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f7fec6a72e1be6aefa3989ea2f630f40427567c2bcd7ea516cb197631e7019c8
|
|
| MD5 |
f9b656b9b9d337b8fad91bd613af1bae
|
|
| BLAKE2b-256 |
5ee897ee8527d0122ab0a432a7579f66bf63151399324acad54f039864ab39ca
|
Provenance
The following attestation bundles were made for ubitofu-0.3.1-py3-none-any.whl:
Publisher:
release.yml on jamesbraid/ubitofu
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ubitofu-0.3.1-py3-none-any.whl -
Subject digest:
f7fec6a72e1be6aefa3989ea2f630f40427567c2bcd7ea516cb197631e7019c8 - Sigstore transparency entry: 2083566237
- Sigstore integration time:
-
Permalink:
jamesbraid/ubitofu@6608dcb9cd873fc1d4af5f367b706786a5dfdd60 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/jamesbraid
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6608dcb9cd873fc1d4af5f367b706786a5dfdd60 -
Trigger Event:
push
-
Statement type: