docs: add Ansible IaC guide for tenant configuration#759
Conversation
📝 WalkthroughWalkthroughAdds a new documentation page for managing NetBird resources declaratively via the community.ansible_netbird Ansible collection, including prerequisites, authentication, an example playbook, module reference, and troubleshooting. Wires the page into site navigation and cross-links it from the automated-setup guide. ChangesAnsible Documentation
Estimated code review effort: 2 (Simple) | ~10 minutes Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
src/components/NavigationDocs.jsxOops! Something went wrong! :( ESLint: 9.39.4 TypeError: Converting circular structure to JSON src/pages/selfhosted/automated-setup.mdxOops! Something went wrong! :( ESLint: 9.39.4 TypeError: Converting circular structure to JSON src/pages/selfhosted/iac/ansible.mdxOops! Something went wrong! :( ESLint: 9.39.4 TypeError: Converting circular structure to JSON Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
8d94e74 to
be642f9
Compare
Document the community.ansible_netbird collection for managing NetBird resources (users, groups, setup keys, policies, networks, DNS, posture checks) declaratively via the REST API. Adds a new Infrastructure as Code subsection under Self-Host NetBird with room for future entries. Cross-links from the Automated Setup PAT bootstrap page since the collection is the natural next step after obtaining the first token.
be642f9 to
7c86bd0
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/pages/selfhosted/iac/ansible.mdx`:
- Around line 1-160: The docs page currently links the NetBird REST API to a
stale internal path, so update the reference in the description/introduction
content to the existing public API docs target instead. Fix the markdown link
that points to the API introduction from the `description` export or nearby
prose in `ansible.mdx`, and use the public API page path already used elsewhere
in the site (or add a redirect if that’s the chosen docs pattern).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 87a1a4f8-6806-4cab-a0b0-782d50c7e7b0
📒 Files selected for processing (3)
src/components/NavigationDocs.jsxsrc/pages/selfhosted/automated-setup.mdxsrc/pages/selfhosted/iac/ansible.mdx
| export const description = 'Manage NetBird users, groups, peers, setup keys, policies, networks, DNS, and posture checks declaratively with the community.ansible_netbird collection.' | ||
|
|
||
| # Configure NetBird with Ansible | ||
|
|
||
| The [`community.ansible_netbird`](https://github.com/netbirdio/ansible-netbird) collection manages NetBird resources — users, groups, peers, setup keys, policies, networks, DNS, posture checks, and identity providers — declaratively against the [NetBird REST API](/api/introduction). Use it to define your tenant configuration in version control and reapply it from CI. | ||
|
|
||
| The collection talks to the API of an existing NetBird instance. It does not install the NetBird client on machines and does not deploy the self-hosted server. It works against any NetBird tenant — cloud or self-hosted — that you can reach with a Personal Access Token. | ||
|
|
||
| ## Requirements | ||
|
|
||
| - Ansible 2.15 or newer. | ||
| - Python 3.9 or newer on the Ansible control node. | ||
| - The Management API URL for your NetBird instance, for example `https://netbird.example.com`. | ||
| - A Personal Access Token (PAT) for a NetBird admin or service user. | ||
|
|
||
| For a brand-new self-hosted instance with no users yet, see [Automated setup with a Personal Access Token](/selfhosted/automated-setup) to obtain the first PAT. | ||
|
|
||
| ## Install the collection | ||
|
|
||
| The collection is not yet published to Ansible Galaxy. Build and install it from source: | ||
|
|
||
| ```bash | ||
| git clone https://github.com/netbirdio/ansible-netbird.git | ||
| cd ansible-netbird | ||
| ansible-galaxy collection build | ||
| ansible-galaxy collection install community-ansible_netbird-*.tar.gz | ||
| ``` | ||
|
|
||
| ## Authenticate | ||
|
|
||
| Modules accept credentials as parameters, environment variables, or role variables. Environment variables are the simplest for local runs and CI: | ||
|
|
||
| ```bash | ||
| export NETBIRD_API_URL="https://netbird.example.com" | ||
| export NETBIRD_API_TOKEN="nbp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" | ||
| ``` | ||
|
|
||
| <Warning> | ||
| Set `api_url` to the base URL of your NetBird instance — do not include `/api`. The collection appends API paths automatically. | ||
| </Warning> | ||
|
|
||
| <Note> | ||
| Store the PAT in Ansible Vault, your CI's secret store, or an environment variable — never commit it to source control. | ||
| </Note> | ||
|
|
||
| When your credentials come from Ansible Vault or group variables rather than the environment, set them once for every NetBird module with `module_defaults` instead of repeating them on each task: | ||
|
|
||
| ```yaml | ||
| module_defaults: | ||
| group/community.ansible_netbird.netbird: | ||
| api_url: "{{ netbird_api_url }}" | ||
| api_token: "{{ netbird_api_token }}" | ||
| ``` | ||
|
|
||
| The collection defines the `community.ansible_netbird.netbird` action group, so these defaults apply to every module. On ansible-core 2.15, place `module_defaults` at the block level rather than the play level to avoid a variable-resolution timing issue when the values come from group variables. | ||
|
|
||
| ## First playbook | ||
|
|
||
| This playbook creates a group and a reusable setup key bound to that group. Save it as `netbird.yml`: | ||
|
|
||
| ```yaml {{ title: 'netbird.yml' }} | ||
| - name: Configure NetBird tenant | ||
| hosts: localhost | ||
| gather_facts: false | ||
| tasks: | ||
| - name: Create a group for servers | ||
| community.ansible_netbird.netbird_group: | ||
| name: servers | ||
| state: present | ||
| register: servers_group | ||
|
|
||
| - name: Create a reusable setup key for servers | ||
| community.ansible_netbird.netbird_setup_key: | ||
| name: server-enrollment | ||
| key_type: reusable | ||
| expires_in: 604800 | ||
| auto_groups: | ||
| - "{{ servers_group.group.id }}" | ||
| state: present | ||
| register: setup_key | ||
|
|
||
| - name: Save the setup key (its secret is returned only on creation) | ||
| ansible.builtin.copy: | ||
| content: "{{ setup_key.setup_key.key }}" | ||
| dest: ./server-enrollment.key | ||
| mode: "0600" | ||
| no_log: true | ||
| when: setup_key.setup_key.key is defined | ||
| ``` | ||
|
|
||
| `auto_groups` takes group IDs, not names, so the setup-key task references the group created in the first task through `servers_group.group.id`. | ||
|
|
||
| Run it against your tenant: | ||
|
|
||
| ```bash | ||
| ansible-playbook netbird.yml | ||
| ``` | ||
|
|
||
| The setup key's secret is returned only the first time the key is created, so the playbook captures it then and writes it out with `0600` permissions. `no_log: true` keeps it out of the job log. Never print a setup key with `debug`, and store it in a secret manager for anything beyond a local test. | ||
|
|
||
| Re-running the playbook is safe — every module is idempotent and reports `changed` only when the API state differs from the playbook. | ||
|
|
||
| The [`examples/`](https://github.com/netbirdio/ansible-netbird/tree/main/examples) directory in the collection repository has fuller playbooks, from dynamic policies to a complete tenant setup. | ||
|
|
||
| ## What you can manage | ||
|
|
||
| The collection ships a module for every resource type: | ||
|
|
||
| - **Users and service users** — `netbird_user` | ||
| - **User invites** — `netbird_invite` (onboard new users; create, delete, or regenerate) | ||
| - **Groups** — `netbird_group` | ||
| - **Setup keys** — `netbird_setup_key` (one-off or reusable, with auto-group assignment) | ||
| - **Peers** — `netbird_peer` (SSH, login and inactivity expiration, approval, name) | ||
| - **Policies** — `netbird_policy` | ||
| - **Posture checks** — `netbird_posture_check` | ||
| - **Networks with routers and resources** — `netbird_network` (the current routing model; replaces the deprecated routes API) | ||
| - **Routes** — `netbird_route` (the deprecated routes API; use `netbird_network` for new setups) | ||
| - **Reverse-proxy services** — `netbird_service` (publish a domain and forward it to one or more targets) | ||
| - **DNS settings and nameserver groups** — `netbird_dns` | ||
| - **DNS zones with records** — `netbird_dns_zone` | ||
| - **Identity providers** — `netbird_idp` | ||
| - **Personal access tokens** — `netbird_token` | ||
| - **Account settings** — `netbird_account` | ||
| - **Read any resource** — `netbird_info` | ||
|
|
||
| See these modules used together in the [`examples/`](https://github.com/netbirdio/ansible-netbird/tree/main/examples) directory of the collection repository. | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| ### Authentication fails with 401 or 403 | ||
|
|
||
| - Confirm the PAT belongs to a user with admin permissions in the tenant. | ||
| - Confirm `NETBIRD_API_URL` points at the base URL without a trailing `/api` segment. | ||
| - Confirm the PAT has not expired or been revoked in the Dashboard. | ||
|
|
||
| ### TLS verification fails | ||
|
|
||
| If your self-hosted instance uses a private or self-signed certificate authority, add the CA to the control node's system trust store, or point the `REQUESTS_CA_BUNDLE` or `SSL_CERT_FILE` environment variable at it. That is what the module's SSL error message tells you to do. Setting `validate_certs: false` also works, but it removes protection against on-path attacks, so keep it as a last resort. | ||
|
|
||
| ### A warning says the API token is sent in cleartext | ||
|
|
||
| Your `api_url` uses `http://` instead of `https://`. The module still runs but warns, because the token travels unencrypted and is exposed to anyone on the network path. Switch to an `https://` URL, or put a TLS-terminating proxy in front of the Management API. This is a warning, not an error. | ||
|
|
||
| ### A request is refused as a redirect | ||
|
|
||
| If a call fails saying the request was redirected and the module refused to follow it, your `api_url` is pointing somewhere that redirects, usually an `http://` URL that bounces to `https://`, or a base URL with an extra path. The module never follows redirects, so the API token is never forwarded to another host. Set `api_url` to the exact HTTPS base URL, for example `https://netbird.example.com` with no trailing path. | ||
|
|
||
| ### Requests time out | ||
|
|
||
| Each API request times out after 30 seconds by default. On a slow link or a very large tenant, raise it with the `timeout` parameter (in seconds) on the module call, or set it once for every module through `module_defaults`. | ||
|
|
||
| ### A setup key's secret is missing from the result | ||
|
|
||
| The key secret is returned by the API **only when the key is first created**. On an idempotent re-run, or for a key that already exists, there is no secret to return and `setup_key.setup_key.key` is undefined. Capture it the first time the key is created and store it in a secret manager. Ansible cannot mask an individual return field, so set `no_log: true` on any task that registers or handles the key rather than relying on it being hidden. | ||
|
|
||
| ### Resources are not updated as expected | ||
|
|
||
| For modules like `netbird_user`, `netbird_group`, and `netbird_setup_key`, omitting a list field (for example `auto_groups` or `peers`) preserves the existing value rather than clearing it. To remove all members, pass an explicit empty list (`[]`). | ||
|
|
||
| A setup key is mostly fixed once created: only its `revoked` state and `auto_groups` change on later runs, so re-running with a different `key_type` or `expires_in` is silently ignored. Rotate the key to change those. |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if the internal link target exists as a page file
fd -t f 'introduction' src/pages/api/
# Check if the external GitHub repo is reachable
curl -sI -o /dev/null -w "%{http_code}" https://github.com/netbirdio/ansible-netbirdRepository: netbirdio/docs
Length of output: 257
🏁 Script executed:
#!/bin/bash
git ls-files | rg '(^|/)introduction\.(md|mdx)$|(^|/)api/.*/introduction\.(md|mdx)$|(^|/)api/introduction(\.(md|mdx))?$'
curl -sL -o /tmp/ansible-netbird.html -w "%{http_code}\n" https://github.com/netbirdio/ansible-netbird
python3 - <<'PY'
from pathlib import Path
p = Path('/tmp/ansible-netbird.html')
text = p.read_text(errors='ignore') if p.exists() else ''
for needle in ['netbirdio/ansible-netbird', 'GitHub', 'ansible-netbird']:
print(needle, needle in text)
PYRepository: netbirdio/docs
Length of output: 278
🏁 Script executed:
git ls-files 'src/pages/**' | rg '(^|/)api(/|$)|(^|/)introduction\.(md|mdx)$|(^|/)index\.(md|mdx)$'
rg -n '"/api/introduction"|/api/introduction|api/introduction' src/pages src/components src/libRepository: netbirdio/docs
Length of output: 2436
🏁 Script executed:
git ls-files | rg '^(src/pages|pages|public|vercel|next)\b|redirect|rewrite|route'
rg -n 'api/introduction|/api/introduction|public-api|redirect|rewrite|trailingSlash|basePath' src public . --glob '!node_modules' --glob '!dist' --glob '!build'Repository: netbirdio/docs
Length of output: 50371
🏁 Script executed:
cat -n next.config.mjs | sed -n '1,220p'Repository: netbirdio/docs
Length of output: 9690
🏁 Script executed:
python3 - <<'PY'
from pathlib import Path
import re
# Check for any source files that would map to /api/introduction
files = [p.as_posix() for p in Path('.').resolve().glob('src/pages/**/*') if p.is_file()]
matches = [f for f in files if re.search(r'(^|/)api(/|$)|(^|/)introduction\.(md|mdx)$', f)]
print("matches:", "\n".join(matches) if matches else "<none>")
# Check next.config.mjs for /api redirects/rewrites
text = Path('next.config.mjs').read_text()
for needle in ['/api/introduction', '/api/:path*', 'destination: \'/api/:path*\'', 'source: \'/api']:
print(needle, needle in text)
PYRepository: netbirdio/docs
Length of output: 349
Fix the stale /api/introduction link
/api/introduction doesn’t resolve in this docs site; point it at the existing public API page (for example /manage/public-api) or add a redirect.
🧰 Tools
🪛 LanguageTool
[style] ~134-~134: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...RL without a trailing /api segment. - Confirm the PAT has not expired or been revoked...
(ENGLISH_WORD_REPEAT_BEGINNING_RULE)
[style] ~150-~150: As an alternative to the over-used intensifier ‘very’, consider replacing this phrase.
Context: ...0 seconds by default. On a slow link or a very large tenant, raise it with the timeout par...
(EN_WEAK_ADJECTIVE)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/pages/selfhosted/iac/ansible.mdx` around lines 1 - 160, The docs page
currently links the NetBird REST API to a stale internal path, so update the
reference in the description/introduction content to the existing public API
docs target instead. Fix the markdown link that points to the API introduction
from the `description` export or nearby prose in `ansible.mdx`, and use the
public API page path already used elsewhere in the site (or add a redirect if
that’s the chosen docs pattern).
Summary
/selfhosted/iac/ansible, documenting thecommunity.ansible_netbirdcollection for managing tenant resources (users, groups, setup keys, policies, networks, DNS, posture checks, IdPs) declaratively against the REST API.selfhosted/automated-setup.mdxso operators who just minted their first PAT see the declarative next step.Scope notes
manage/peers/register-machines-using-setup-keys.mdx.examples/instead of mirroring playbooks that would rot.Summary by CodeRabbit
New Features
Documentation