mirror of
https://github.com/netbirdio/ansible-netbird.git
synced 2026-05-22 18:43:36 -07:00
restructure to roles
This commit is contained in:
@@ -587,9 +587,9 @@ The `examples/` directory contains complete playbook examples:
|
||||
- `inventory_from_netbird.yml` - Export peers as Ansible inventory
|
||||
- `peer_management.yml` - Manage and audit peers
|
||||
|
||||
## Config as Code (IaC Playbooks)
|
||||
## Config as Code (IaC Roles)
|
||||
|
||||
This collection includes playbooks for managing your entire NetBird configuration as YAML files in Git — with preview/diff, strict mode, and automatic name-to-ID resolution.
|
||||
This collection includes roles and playbooks for managing your entire NetBird configuration as YAML files in Git — with preview/diff, strict mode, and automatic name-to-ID resolution.
|
||||
|
||||
### Quick Start
|
||||
|
||||
@@ -613,6 +613,21 @@ ansible-playbook community.ansible_netbird.configure_netbird \
|
||||
-e "commit=true"
|
||||
```
|
||||
|
||||
### Using Roles Directly
|
||||
|
||||
For inventory-based workflows (e.g., AAP), use the roles directly in your own playbooks:
|
||||
|
||||
```yaml
|
||||
- name: Configure NetBird
|
||||
hosts: netbird_control_nodes
|
||||
gather_facts: false
|
||||
run_once: true
|
||||
roles:
|
||||
- role: community.ansible_netbird.configure
|
||||
vars:
|
||||
config_dir: "{{ playbook_dir }}/../netbird_config/{{ netbird_env }}"
|
||||
```
|
||||
|
||||
### Features
|
||||
|
||||
- **Preview mode** (default) — shows a read-only diff of what would change before applying
|
||||
@@ -620,7 +635,7 @@ ansible-playbook community.ansible_netbird.configure_netbird \
|
||||
- **Name-based config** — use plain names ("developers") instead of API IDs; resolved automatically
|
||||
- **Dependency ordering** — resources applied in correct order (settings → posture checks → groups → DNS → networks → policies)
|
||||
- **Export utility** — captures current API state as clean, ready-to-use YAML config files
|
||||
- **Inventory support** — set `target_hosts` to run against your inventory group instead of localhost
|
||||
- **Roles** — use `community.ansible_netbird.configure` and `community.ansible_netbird.export` directly in your own playbooks for full control
|
||||
|
||||
### Config Directory Structure
|
||||
|
||||
|
||||
@@ -308,18 +308,24 @@ ansible-playbook community.ansible_netbird.configure_netbird \
|
||||
-e "netbird_api_token=your-token"
|
||||
```
|
||||
|
||||
Or create thin wrapper playbooks that set `target_hosts` for you:
|
||||
Or create your own playbooks that use the roles directly — this is the recommended approach for inventory-based workflows:
|
||||
|
||||
```yaml
|
||||
# configure_netbird.yml (wrapper)
|
||||
- import_playbook: community.ansible_netbird.configure_netbird
|
||||
vars:
|
||||
target_hosts: netbird_control_nodes
|
||||
config_dir: "{{ playbook_dir }}/../netbird_config/{{ netbird_env }}"
|
||||
# configure_netbird.yml (using the role directly)
|
||||
- name: Configure NetBird
|
||||
hosts: netbird_control_nodes
|
||||
gather_facts: false
|
||||
run_once: true
|
||||
roles:
|
||||
- role: community.ansible_netbird.configure
|
||||
vars:
|
||||
config_dir: "{{ playbook_dir }}/../netbird_config/{{ netbird_env }}"
|
||||
```
|
||||
|
||||
Then run with just a limit: `ansible-playbook configure_netbird.yml -i inventory -l preprod`
|
||||
|
||||
Using roles directly gives you full control over `hosts`, `gather_facts`, and variable resolution — and avoids `import_playbook` path resolution issues in AAP.
|
||||
|
||||
## Multi-Environment Setup
|
||||
|
||||
For managing multiple environments (e.g., production and staging), create separate config directories:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,386 +1,23 @@
|
||||
---
|
||||
# Playbook: Export Current NetBird Configuration
|
||||
#
|
||||
# Captures the current NetBird API state and produces clean, ready-to-use
|
||||
# config files that can be used directly with the configure_netbird playbook.
|
||||
# Also saves raw API data in a raw/ subdirectory for debugging.
|
||||
# Thin wrapper around the community.ansible_netbird.export role.
|
||||
# See the role documentation for full details.
|
||||
#
|
||||
# Usage:
|
||||
# ansible-playbook community.ansible_netbird.export_netbird_config \
|
||||
# -e "netbird_api_url=https://netbird.example.com" \
|
||||
# -e "netbird_api_token=your-token"
|
||||
#
|
||||
# Output is written to /tmp/netbird_config_export/ by default.
|
||||
# Override with: -e "export_dir=/custom/path"
|
||||
#
|
||||
# Required variables (via extra vars):
|
||||
# - netbird_api_url: NetBird API URL (e.g., https://netbird.example.com)
|
||||
# - netbird_api_token: NetBird API token
|
||||
# Override output directory:
|
||||
# ... -e "export_dir=/custom/path"
|
||||
#
|
||||
# Optional variables:
|
||||
# - target_hosts: Inventory group or host to run on (default: localhost)
|
||||
# - export_dir: Output directory (default: /tmp/netbird_config_export)
|
||||
# - netbird_validate_certs: Validate API TLS certs (default: true)
|
||||
# - target_hosts: Host or group to run on (default: localhost)
|
||||
|
||||
- name: Export Current NetBird Configuration
|
||||
hosts: "{{ target_hosts | default('localhost') }}"
|
||||
gather_facts: true
|
||||
run_once: true
|
||||
|
||||
module_defaults:
|
||||
group/community.ansible_netbird.netbird:
|
||||
api_url: "{{ netbird_api_url }}"
|
||||
api_token: "{{ netbird_api_token }}"
|
||||
validate_certs: "{{ netbird_validate_certs }}"
|
||||
|
||||
vars:
|
||||
# netbird_api_url: "" # Required - set via -e
|
||||
# netbird_api_token: "" # Required - set via -e
|
||||
netbird_validate_certs: true
|
||||
export_dir: "/tmp/netbird_config_export"
|
||||
|
||||
pre_tasks:
|
||||
- name: Validate API configuration
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- netbird_api_url is defined
|
||||
- netbird_api_url | length > 0
|
||||
- netbird_api_token is defined
|
||||
- netbird_api_token | length > 0
|
||||
fail_msg: |
|
||||
NetBird API configuration is missing!
|
||||
Required extra vars:
|
||||
netbird_api_url: "https://netbird.example.com"
|
||||
netbird_api_token: "your-api-token"
|
||||
|
||||
- name: Create export directories
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
loop:
|
||||
- "{{ export_dir }}"
|
||||
- "{{ export_dir }}/access_control"
|
||||
- "{{ export_dir }}/dns"
|
||||
- "{{ export_dir }}/raw"
|
||||
|
||||
tasks:
|
||||
# =========================================================================
|
||||
# Fetch all resources from the NetBird API
|
||||
# =========================================================================
|
||||
- name: Fetch account settings
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: accounts
|
||||
register: accounts_data
|
||||
|
||||
- name: Fetch groups
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: groups
|
||||
register: groups_data
|
||||
|
||||
- name: Fetch posture checks
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: posture_checks
|
||||
register: posture_checks_data
|
||||
|
||||
- name: Fetch setup keys
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: setup_keys
|
||||
register: setup_keys_data
|
||||
|
||||
- name: Fetch DNS nameserver groups
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: dns_nameservers
|
||||
register: dns_nameservers_data
|
||||
|
||||
- name: Fetch DNS settings
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: dns_settings
|
||||
register: dns_settings_data
|
||||
|
||||
- name: Fetch DNS zones
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: dns_zones
|
||||
register: dns_zones_data
|
||||
|
||||
- name: Fetch routes (deprecated API)
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: routes
|
||||
register: routes_data
|
||||
|
||||
- name: Fetch networks
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: networks
|
||||
register: networks_data
|
||||
|
||||
- name: Fetch routers for each network
|
||||
ansible.builtin.uri:
|
||||
url: "{{ netbird_api_url }}/api/networks/{{ item.id }}/routers"
|
||||
method: GET
|
||||
headers:
|
||||
Authorization: "Token {{ netbird_api_token }}"
|
||||
Accept: "application/json"
|
||||
validate_certs: "{{ netbird_validate_certs }}"
|
||||
return_content: true
|
||||
loop: "{{ networks_data.data }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
register: networks_routers_raw
|
||||
when: networks_data.data | length > 0
|
||||
|
||||
- name: Fetch resources for each network
|
||||
ansible.builtin.uri:
|
||||
url: "{{ netbird_api_url }}/api/networks/{{ item.id }}/resources"
|
||||
method: GET
|
||||
headers:
|
||||
Authorization: "Token {{ netbird_api_token }}"
|
||||
Accept: "application/json"
|
||||
validate_certs: "{{ netbird_validate_certs }}"
|
||||
return_content: true
|
||||
loop: "{{ networks_data.data }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
register: networks_resources_raw
|
||||
when: networks_data.data | length > 0
|
||||
|
||||
- name: Enrich networks with routers and resources
|
||||
ansible.builtin.set_fact:
|
||||
networks_enriched: >-
|
||||
{% set result = [] -%}
|
||||
{% for net in networks_data.data -%}
|
||||
{% set routers = networks_routers_raw.results[loop.index0].get('json', []) -%}
|
||||
{% set resources = networks_resources_raw.results[loop.index0].get('json', []) -%}
|
||||
{% set _ = result.append(net | combine({'routers': routers, 'resources': resources})) -%}
|
||||
{% endfor -%}
|
||||
{{ result | to_json }}
|
||||
when: networks_data.data | length > 0
|
||||
|
||||
- name: Set empty enriched networks when none exist
|
||||
ansible.builtin.set_fact:
|
||||
networks_enriched: []
|
||||
when: networks_data.data | length == 0
|
||||
|
||||
- name: Fetch policies
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: policies
|
||||
register: policies_data
|
||||
|
||||
- name: Fetch users
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: users
|
||||
register: users_data
|
||||
|
||||
- name: Fetch peers
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: peers
|
||||
register: peers_data
|
||||
|
||||
# =========================================================================
|
||||
# Build ID-to-name lookup maps
|
||||
# =========================================================================
|
||||
- name: Initialize lookup maps
|
||||
ansible.builtin.set_fact:
|
||||
group_id_map: {}
|
||||
posture_check_id_map: {}
|
||||
peer_id_map: {}
|
||||
|
||||
- name: Build group ID-to-name map
|
||||
ansible.builtin.set_fact:
|
||||
group_id_map: "{{ group_id_map | combine({item.id: item.name}) }}"
|
||||
loop: "{{ groups_data.data }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
when: groups_data.data | length > 0
|
||||
|
||||
- name: Build posture check ID-to-name map
|
||||
ansible.builtin.set_fact:
|
||||
posture_check_id_map: "{{ posture_check_id_map | combine({item.id: item.name}) }}"
|
||||
loop: "{{ posture_checks_data.data }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
when: posture_checks_data.data | length > 0
|
||||
|
||||
- name: Build peer ID-to-name map
|
||||
ansible.builtin.set_fact:
|
||||
peer_id_map: "{{ peer_id_map | combine({item.id: item.name}) }}"
|
||||
loop: "{{ peers_data.data }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
when: peers_data.data | length > 0
|
||||
|
||||
# =========================================================================
|
||||
# Write clean config files (ready to use with configure_netbird playbook)
|
||||
# =========================================================================
|
||||
- name: Export clean settings
|
||||
ansible.builtin.template:
|
||||
src: templates/export/settings.yml.j2
|
||||
dest: "{{ export_dir }}/settings.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export clean groups
|
||||
ansible.builtin.template:
|
||||
src: templates/export/access_control/groups.yml.j2
|
||||
dest: "{{ export_dir }}/access_control/groups.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export clean posture checks
|
||||
ansible.builtin.template:
|
||||
src: templates/export/access_control/posture_checks.yml.j2
|
||||
dest: "{{ export_dir }}/access_control/posture_checks.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export clean policies
|
||||
ansible.builtin.template:
|
||||
src: templates/export/access_control/policies.yml.j2
|
||||
dest: "{{ export_dir }}/access_control/policies.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export clean setup keys
|
||||
ansible.builtin.template:
|
||||
src: templates/export/setup_keys.yml.j2
|
||||
dest: "{{ export_dir }}/setup_keys.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export clean DNS nameservers
|
||||
ansible.builtin.template:
|
||||
src: templates/export/dns/nameservers.yml.j2
|
||||
dest: "{{ export_dir }}/dns/nameservers.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export clean DNS zones
|
||||
ansible.builtin.template:
|
||||
src: templates/export/dns/zones.yml.j2
|
||||
dest: "{{ export_dir }}/dns/zones.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export clean DNS settings
|
||||
ansible.builtin.template:
|
||||
src: templates/export/dns/settings.yml.j2
|
||||
dest: "{{ export_dir }}/dns/settings.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export clean networks
|
||||
ansible.builtin.template:
|
||||
src: templates/export/networks.yml.j2
|
||||
dest: "{{ export_dir }}/networks.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export clean routes (deprecated API)
|
||||
ansible.builtin.template:
|
||||
src: templates/export/routes.yml.j2
|
||||
dest: "{{ export_dir }}/routes.yml"
|
||||
mode: "0644"
|
||||
|
||||
# =========================================================================
|
||||
# Write raw API data (for debugging/reference)
|
||||
# =========================================================================
|
||||
- name: Export raw account settings
|
||||
ansible.builtin.copy:
|
||||
content: "{{ accounts_data.data | to_nice_yaml(indent=2) }}"
|
||||
dest: "{{ export_dir }}/raw/account_settings_raw.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export raw groups
|
||||
ansible.builtin.copy:
|
||||
content: "{{ groups_data.data | to_nice_yaml(indent=2) }}"
|
||||
dest: "{{ export_dir }}/raw/groups_raw.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export raw posture checks
|
||||
ansible.builtin.copy:
|
||||
content: "{{ posture_checks_data.data | to_nice_yaml(indent=2) }}"
|
||||
dest: "{{ export_dir }}/raw/posture_checks_raw.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export raw setup keys
|
||||
ansible.builtin.copy:
|
||||
content: "{{ setup_keys_data.data | to_nice_yaml(indent=2) }}"
|
||||
dest: "{{ export_dir }}/raw/setup_keys_raw.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export raw DNS
|
||||
ansible.builtin.copy:
|
||||
content: "{{ dns_nameservers_data.data | to_nice_yaml(indent=2) }}"
|
||||
dest: "{{ export_dir }}/raw/dns_nameservers_raw.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export raw DNS zones
|
||||
ansible.builtin.copy:
|
||||
content: "{{ dns_zones_data.data | to_nice_yaml(indent=2) }}"
|
||||
dest: "{{ export_dir }}/raw/dns_zones_raw.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export raw networks
|
||||
ansible.builtin.copy:
|
||||
content: "{{ networks_data.data | to_nice_yaml(indent=2) }}"
|
||||
dest: "{{ export_dir }}/raw/networks_raw.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export raw routes
|
||||
ansible.builtin.copy:
|
||||
content: "{{ routes_data.data | to_nice_yaml(indent=2) }}"
|
||||
dest: "{{ export_dir }}/raw/routes_raw.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export raw policies
|
||||
ansible.builtin.copy:
|
||||
content: "{{ policies_data.data | to_nice_yaml(indent=2) }}"
|
||||
dest: "{{ export_dir }}/raw/policies_raw.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export users reference
|
||||
ansible.builtin.copy:
|
||||
content: |
|
||||
---
|
||||
# NetBird Users Reference ({{ users_data.data | length }} users)
|
||||
# Exported on {{ ansible_date_time.date }}
|
||||
# Users are managed via IdP sync, not via config-as-code.
|
||||
{{ users_data.data | to_nice_yaml(indent=2) }}
|
||||
dest: "{{ export_dir }}/raw/users_reference.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export peers reference
|
||||
ansible.builtin.copy:
|
||||
content: |
|
||||
---
|
||||
# NetBird Peers Reference ({{ peers_data.data | length }} peers)
|
||||
# Exported on {{ ansible_date_time.date }}
|
||||
# Peers register via setup keys — not managed via config-as-code.
|
||||
# Use peer IDs from this file for network router configuration.
|
||||
{{ peers_data.data | to_nice_yaml(indent=2) }}
|
||||
dest: "{{ export_dir }}/raw/peers_reference.yml"
|
||||
mode: "0644"
|
||||
|
||||
# =========================================================================
|
||||
# Summary
|
||||
# =========================================================================
|
||||
- name: Display export summary
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "Export complete! Files written to: {{ export_dir }}/"
|
||||
- ""
|
||||
- "Clean config files (ready to use with configure_netbird playbook):"
|
||||
- " - settings.yml (account settings)"
|
||||
- " - access_control/"
|
||||
- " groups.yml ({{ groups_data.data | rejectattr('name', 'equalto', 'All') | rejectattr('issued', 'equalto', 'jwt') | list | length }} groups)"
|
||||
- " posture_checks.yml ({{ posture_checks_data.data | length }} posture checks)"
|
||||
- " policies.yml ({{ policies_data.data | length }} policies)"
|
||||
- " - setup_keys.yml ({{ setup_keys_data.data | length }} setup keys — read-only reference)"
|
||||
- " - dns/"
|
||||
- " nameservers.yml ({{ dns_nameservers_data.data | length }} nameserver groups)"
|
||||
- " zones.yml ({{ dns_zones_data.data | length }} zones)"
|
||||
- " settings.yml (DNS settings)"
|
||||
- " - networks.yml ({{ networks_data.data | length }} networks)"
|
||||
- " - routes.yml ({{ routes_data.data | length }} routes — deprecated API)"
|
||||
- ""
|
||||
- "Raw API data (for debugging):"
|
||||
- " raw/ (account, groups, posture_checks, setup_keys, dns, routes, networks, policies, users, peers)"
|
||||
- ""
|
||||
- "Next steps:"
|
||||
- " 1. Review the clean config files"
|
||||
- " 2. Copy them to your config directory (preserving directory structure)"
|
||||
- " 3. Run the configure playbook to preview:"
|
||||
- " ansible-playbook community.ansible_netbird.configure_netbird \\"
|
||||
- " -e \"config_dir={{ export_dir }}\" \\"
|
||||
- " -e \"netbird_api_url={{ netbird_api_url }}\" \\"
|
||||
- " -e \"netbird_api_token=your-token\""
|
||||
roles:
|
||||
- community.ansible_netbird.export
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
# Required - path to your config directory (see config_skeleton/ for structure)
|
||||
# config_dir: ""
|
||||
|
||||
# Apply changes (default: false — preview only)
|
||||
commit: false
|
||||
|
||||
# Remove resources not in config (default: false)
|
||||
strict: false
|
||||
|
||||
# Validate API TLS certificates
|
||||
netbird_validate_certs: true
|
||||
@@ -0,0 +1,476 @@
|
||||
---
|
||||
# Role: community.ansible_netbird.configure
|
||||
#
|
||||
# Applies NetBird logical configuration from YAML files via the NetBird REST API.
|
||||
# Manages groups, policies, networks, DNS (nameservers + zones), posture checks,
|
||||
# and account settings.
|
||||
#
|
||||
# Runs in preview mode by default — shows a read-only diff of what would change.
|
||||
# To apply changes, set commit=true. Strict mode (strict=true) removes resources
|
||||
# not defined in YAML.
|
||||
#
|
||||
# Execution order (respects dependencies):
|
||||
# 1. Account settings (no dependencies)
|
||||
# 2. Posture checks (no dependencies, needed by policies)
|
||||
# 3. Groups (no dependencies, needed by everything else)
|
||||
# 4. DNS (depend on groups)
|
||||
# 5. Networks (depend on groups)
|
||||
# 6. Policies (depend on groups + posture checks)
|
||||
#
|
||||
# Required variables:
|
||||
# - config_dir: Path to your config directory
|
||||
# - netbird_api_url: NetBird API URL
|
||||
# - netbird_api_token: NetBird API token
|
||||
#
|
||||
# Optional variables:
|
||||
# - commit: Apply changes (default: false)
|
||||
# - strict: Remove resources not in config (default: false)
|
||||
# - netbird_validate_certs: Validate API TLS certs (default: true)
|
||||
|
||||
# =========================================================================
|
||||
# Validation (outside the module_defaults block so we get nice error messages
|
||||
# if variables are undefined)
|
||||
# =========================================================================
|
||||
- name: Validate API configuration
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- netbird_api_url is defined
|
||||
- netbird_api_url | length > 0
|
||||
- netbird_api_token is defined
|
||||
- netbird_api_token | length > 0
|
||||
- config_dir is defined
|
||||
- config_dir | length > 0
|
||||
fail_msg: |
|
||||
Configuration is missing!
|
||||
Required variables:
|
||||
config_dir: "/path/to/netbird_config"
|
||||
netbird_api_url: "https://netbird.example.com"
|
||||
netbird_api_token: "your-api-token"
|
||||
|
||||
- name: Display target environment
|
||||
ansible.builtin.debug:
|
||||
msg: >-
|
||||
NetBird {{ config_dir | basename | upper }} —
|
||||
{{ 'APPLY MODE' if commit | bool else 'PREVIEW MODE (read-only, no changes)' }}{{ ' + STRICT (unmanaged resources will be removed)' if strict | bool else '' }}
|
||||
— config from {{ config_dir }}/
|
||||
|
||||
- name: Check config directory exists
|
||||
ansible.builtin.stat:
|
||||
path: "{{ config_dir }}"
|
||||
register: config_dir_stat
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Fail if config directory doesn't exist
|
||||
ansible.builtin.fail:
|
||||
msg: "Config directory not found: {{ config_dir }}"
|
||||
when: not config_dir_stat.stat.exists
|
||||
|
||||
# =========================================================================
|
||||
# Load all configuration files
|
||||
# =========================================================================
|
||||
- name: Load settings
|
||||
ansible.builtin.include_vars:
|
||||
file: "{{ config_dir }}/settings.yml"
|
||||
|
||||
- name: Load access control (groups, posture checks, policies)
|
||||
ansible.builtin.include_vars:
|
||||
dir: "{{ config_dir }}/access_control"
|
||||
extensions: ['yml', 'yaml']
|
||||
|
||||
- name: Load DNS configuration (nameservers, zones, settings)
|
||||
ansible.builtin.include_vars:
|
||||
dir: "{{ config_dir }}/dns"
|
||||
extensions: ['yml', 'yaml']
|
||||
|
||||
- name: Load networks
|
||||
ansible.builtin.include_vars:
|
||||
file: "{{ config_dir }}/networks.yml"
|
||||
|
||||
- name: Display configuration summary
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "Configuration loaded from: {{ config_dir }}/"
|
||||
- " Settings: {{ 'configured' if netbird_settings | default({}) | length > 0 else 'empty' }}"
|
||||
- " Groups: {{ netbird_groups | default([]) | length }}"
|
||||
- " Posture checks: {{ netbird_posture_checks | default([]) | length }}"
|
||||
- " Policies: {{ netbird_policies | default([]) | length }}"
|
||||
- " DNS nameservers: {{ netbird_dns_nameserver_groups | default([]) | length }}"
|
||||
- " DNS zones: {{ netbird_dns_zones | default([]) | length }}"
|
||||
- " Networks: {{ netbird_networks | default([]) | length }}"
|
||||
|
||||
# =========================================================================
|
||||
# All API-calling tasks wrapped in a block with module_defaults
|
||||
# =========================================================================
|
||||
- name: Configure NetBird resources
|
||||
block:
|
||||
# =====================================================================
|
||||
# PHASE 1: Apply foundational resources (commit mode only)
|
||||
# =====================================================================
|
||||
|
||||
# --- 1. Settings (no dependencies) ---
|
||||
- name: Apply settings
|
||||
community.ansible_netbird.netbird_account:
|
||||
peer_login_expiration_enabled: "{{ netbird_settings.peer_login_expiration_enabled | default(omit) }}"
|
||||
peer_login_expiration: "{{ netbird_settings.peer_login_expiration | default(omit) }}"
|
||||
peer_inactivity_expiration_enabled: "{{ netbird_settings.peer_inactivity_expiration_enabled | default(omit) }}"
|
||||
peer_inactivity_expiration: "{{ netbird_settings.peer_inactivity_expiration | default(omit) }}"
|
||||
regular_users_view_blocked: "{{ netbird_settings.regular_users_view_blocked | default(omit) }}"
|
||||
groups_propagation_enabled: "{{ netbird_settings.groups_propagation_enabled | default(omit) }}"
|
||||
jwt_groups_enabled: "{{ netbird_settings.jwt_groups_enabled | default(omit) }}"
|
||||
jwt_groups_claim_name: "{{ netbird_settings.jwt_groups_claim_name | default(omit) }}"
|
||||
jwt_allow_groups: "{{ netbird_settings.jwt_allow_groups | default([]) | map('extract', group_ids) | list if netbird_settings.jwt_allow_groups is defined else omit }}"
|
||||
routing_peer_dns_resolution_enabled: "{{ netbird_settings.routing_peer_dns_resolution_enabled | default(omit) }}"
|
||||
dns_domain: "{{ netbird_settings.dns_domain | default(omit) }}"
|
||||
network_range: "{{ netbird_settings.network_range | default(omit) }}"
|
||||
lazy_connection_enabled: "{{ netbird_settings.lazy_connection_enabled | default(omit) }}"
|
||||
auto_update_always: "{{ netbird_settings.auto_update_always | default(omit) }}"
|
||||
auto_update_version: "{{ netbird_settings.auto_update_version | default(omit) }}"
|
||||
peer_expose_enabled: "{{ netbird_settings.peer_expose_enabled | default(omit) }}"
|
||||
peer_expose_groups: "{{ netbird_settings.peer_expose_groups | default([]) | map('extract', group_ids) | list if netbird_settings.peer_expose_groups is defined else omit }}"
|
||||
extra_peer_approval_enabled: "{{ netbird_settings.extra_peer_approval_enabled | default(omit) }}"
|
||||
extra_user_approval_required: "{{ netbird_settings.extra_user_approval_required | default(omit) }}"
|
||||
extra_network_traffic_logs_enabled: "{{ netbird_settings.extra_network_traffic_logs_enabled | default(omit) }}"
|
||||
extra_network_traffic_logs_groups: "{{ netbird_settings.extra_network_traffic_logs_groups | default([]) | map('extract', group_ids) | list if netbird_settings.extra_network_traffic_logs_groups is defined else omit }}"
|
||||
extra_network_traffic_packet_counter_enabled: "{{ netbird_settings.extra_network_traffic_packet_counter_enabled | default(omit) }}"
|
||||
state: present
|
||||
when:
|
||||
- commit | bool
|
||||
- netbird_settings | default({}) | length > 0
|
||||
|
||||
# --- 2. Posture Checks (no dependencies, needed by policies) ---
|
||||
- name: Manage posture checks
|
||||
community.ansible_netbird.netbird_posture_check:
|
||||
name: "{{ item.name }}"
|
||||
description: "{{ item.description | default(omit) }}"
|
||||
checks: "{{ item.checks }}"
|
||||
state: "{{ item.state | default('present') }}"
|
||||
loop: "{{ netbird_posture_checks | default([]) }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
when:
|
||||
- commit | bool
|
||||
- netbird_posture_checks | default([]) | length > 0
|
||||
|
||||
# --- 3. Groups (no dependencies, needed by DNS/networks/policies) ---
|
||||
- name: Manage groups
|
||||
community.ansible_netbird.netbird_group:
|
||||
name: "{{ item.name }}"
|
||||
peers: "{{ item.peers | default(omit) }}"
|
||||
state: "{{ item.state | default('present') }}"
|
||||
loop: "{{ netbird_groups | default([]) }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
when:
|
||||
- commit | bool
|
||||
- netbird_groups | default([]) | length > 0
|
||||
|
||||
# =====================================================================
|
||||
# PHASE 2: Fetch API state + build lookup maps (both modes)
|
||||
# =====================================================================
|
||||
- name: Initialize lookup maps
|
||||
ansible.builtin.set_fact:
|
||||
group_ids: {}
|
||||
posture_check_ids: {}
|
||||
|
||||
- name: Fetch current groups for ID resolution
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: groups
|
||||
register: api_groups
|
||||
|
||||
- name: Build group name→ID map
|
||||
ansible.builtin.set_fact:
|
||||
group_ids: "{{ group_ids | combine({item.name: item.id}) }}"
|
||||
loop: "{{ api_groups.data }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
when: api_groups.data | length > 0
|
||||
|
||||
- name: Fetch current posture checks for ID resolution
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: posture_checks
|
||||
register: api_posture_checks
|
||||
|
||||
- name: Build posture check name→ID map
|
||||
ansible.builtin.set_fact:
|
||||
posture_check_ids: "{{ posture_check_ids | combine({item.name: item.id}) }}"
|
||||
loop: "{{ api_posture_checks.data }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
when: api_posture_checks.data | length > 0
|
||||
|
||||
- name: Display ID maps
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "Group IDs resolved: {{ group_ids | length }} groups"
|
||||
- "Posture check IDs resolved: {{ posture_check_ids | length }} checks"
|
||||
|
||||
# =====================================================================
|
||||
# Resolve group/posture-check names to IDs in config variables
|
||||
# =====================================================================
|
||||
- name: Resolve group names in policies and networks
|
||||
ansible.builtin.set_fact:
|
||||
_resolved_policies: >-
|
||||
{% set result = [] -%}
|
||||
{% for policy in netbird_policies | default([]) -%}
|
||||
{% set resolved_rules = [] -%}
|
||||
{% for rule in policy.rules | default([]) -%}
|
||||
{% set _ = resolved_rules.append(rule | combine({
|
||||
'sources': rule.sources | default([]) | map('extract', group_ids) | list,
|
||||
'destinations': rule.destinations | default([]) | map('extract', group_ids) | list
|
||||
})) -%}
|
||||
{% endfor -%}
|
||||
{% set _ = result.append(policy | combine({
|
||||
'source_posture_checks': policy.source_posture_checks | default([]) | map('extract', posture_check_ids) | list,
|
||||
'rules': resolved_rules
|
||||
})) -%}
|
||||
{% endfor -%}
|
||||
{{ result | to_json }}
|
||||
_resolved_networks: >-
|
||||
{% set result = [] -%}
|
||||
{% for network in netbird_networks | default([]) -%}
|
||||
{% set resolved_resources = [] -%}
|
||||
{% for resource in network.resources | default([]) -%}
|
||||
{% set _ = resolved_resources.append(resource | combine({
|
||||
'groups': resource.groups | default([]) | map('extract', group_ids) | list
|
||||
})) -%}
|
||||
{% endfor -%}
|
||||
{% set resolved_routers = [] -%}
|
||||
{% for router in network.routers | default([]) -%}
|
||||
{% if router.peer_groups is defined -%}
|
||||
{% set _ = resolved_routers.append(router | combine({
|
||||
'peer_groups': router.peer_groups | map('extract', group_ids) | list
|
||||
})) -%}
|
||||
{% else -%}
|
||||
{% set _ = resolved_routers.append(router) -%}
|
||||
{% endif -%}
|
||||
{% endfor -%}
|
||||
{% set _ = result.append(network | combine({
|
||||
'resources': resolved_resources,
|
||||
'routers': resolved_routers
|
||||
})) -%}
|
||||
{% endfor -%}
|
||||
{{ result | to_json }}
|
||||
when: commit | bool
|
||||
|
||||
# =====================================================================
|
||||
# PHASE 3: Fetch remaining API state (preview or strict mode)
|
||||
# =====================================================================
|
||||
- name: Fetch current DNS nameserver groups
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: dns_nameservers
|
||||
register: api_dns_nameservers
|
||||
when: not commit | bool or strict | bool
|
||||
|
||||
- name: Fetch current networks
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: networks
|
||||
register: api_networks
|
||||
when: not commit | bool or strict | bool
|
||||
|
||||
- name: Fetch current DNS zones
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: dns_zones
|
||||
register: api_dns_zones
|
||||
when: not commit | bool or strict | bool
|
||||
|
||||
- name: Fetch current policies
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: policies
|
||||
register: api_policies
|
||||
when: not commit | bool or strict | bool
|
||||
|
||||
- name: Fetch current account settings
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: accounts
|
||||
register: api_accounts
|
||||
when: not commit | bool or strict | bool
|
||||
|
||||
- name: Generate diff report
|
||||
ansible.builtin.include_tasks: preview_diff_report.yml
|
||||
when: not commit | bool
|
||||
|
||||
# =====================================================================
|
||||
# PHASE 4: Apply dependent resources (commit mode only)
|
||||
# =====================================================================
|
||||
|
||||
# --- 4. DNS (depend on groups) ---
|
||||
- name: Manage DNS nameserver groups
|
||||
community.ansible_netbird.netbird_dns:
|
||||
resource_type: nameserver_group
|
||||
name: "{{ item.name }}"
|
||||
description: "{{ item.description | default(omit) }}"
|
||||
nameservers: "{{ item.nameservers }}"
|
||||
groups: "{{ item.groups | default([]) | map('extract', group_ids) | list }}"
|
||||
domains: "{{ item.domains | default(omit) }}"
|
||||
enabled: "{{ item.enabled | default(true) }}"
|
||||
primary: "{{ item.primary | default(false) }}"
|
||||
search_domains_enabled: "{{ item.search_domains_enabled | default(omit) }}"
|
||||
state: "{{ item.state | default('present') }}"
|
||||
loop: "{{ netbird_dns_nameserver_groups | default([]) }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
when:
|
||||
- commit | bool
|
||||
- netbird_dns_nameserver_groups | default([]) | length > 0
|
||||
|
||||
- name: Manage DNS settings
|
||||
community.ansible_netbird.netbird_dns:
|
||||
resource_type: settings
|
||||
disabled_management_groups: "{{ netbird_dns_disabled_management_groups | default([]) | map('extract', group_ids) | list }}"
|
||||
state: present
|
||||
when:
|
||||
- commit | bool
|
||||
- netbird_dns_disabled_management_groups is defined
|
||||
|
||||
# --- 5. DNS Zones (depend on groups) ---
|
||||
- name: Manage DNS zones
|
||||
community.ansible_netbird.netbird_dns_zone:
|
||||
name: "{{ item.name }}"
|
||||
domain: "{{ item.domain | default(omit) }}"
|
||||
enabled: "{{ item.enabled | default(true) }}"
|
||||
enable_search_domain: "{{ item.enable_search_domain | default(false) }}"
|
||||
distribution_groups: "{{ item.distribution_groups | default([]) | map('extract', group_ids) | list }}"
|
||||
records: "{{ item.records | default(omit) }}"
|
||||
state: "{{ item.state | default('present') }}"
|
||||
loop: "{{ netbird_dns_zones | default([]) }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
when:
|
||||
- commit | bool
|
||||
- netbird_dns_zones | default([]) | length > 0
|
||||
|
||||
# --- 6. Networks (depend on groups) ---
|
||||
- name: Manage networks
|
||||
community.ansible_netbird.netbird_network:
|
||||
name: "{{ item.name }}"
|
||||
description: "{{ item.description | default(omit) }}"
|
||||
routers: "{{ item.routers | default(omit) }}"
|
||||
resources: "{{ item.resources | default(omit) }}"
|
||||
state: "{{ item.state | default('present') }}"
|
||||
loop: "{{ _resolved_networks | default(netbird_networks | default([])) }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
when:
|
||||
- commit | bool
|
||||
- netbird_networks | default([]) | length > 0
|
||||
|
||||
# --- 7. Policies (depend on groups + posture checks) ---
|
||||
- name: Manage policies
|
||||
community.ansible_netbird.netbird_policy:
|
||||
name: "{{ item.name }}"
|
||||
description: "{{ item.description | default('') }}"
|
||||
enabled: "{{ item.enabled | default(true) }}"
|
||||
source_posture_checks: "{{ item.source_posture_checks | default(omit) }}"
|
||||
rules: "{{ item.rules | default(omit) }}"
|
||||
state: "{{ item.state | default('present') }}"
|
||||
loop: "{{ _resolved_policies | default(netbird_policies | default([])) }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
when:
|
||||
- commit | bool
|
||||
- netbird_policies | default([]) | length > 0
|
||||
|
||||
# =====================================================================
|
||||
# PHASE 5: Strict mode — remove unmanaged resources
|
||||
# =====================================================================
|
||||
- name: "Strict: Build list of protected group names"
|
||||
ansible.builtin.set_fact:
|
||||
_protected_groups: "{{ api_groups.data | selectattr('issued', 'defined') | selectattr('issued', 'equalto', 'jwt') | map(attribute='name') | list + ['All'] }}"
|
||||
_yaml_group_names: "{{ netbird_groups | default([]) | map(attribute='name') | list }}"
|
||||
_yaml_pc_names: "{{ netbird_posture_checks | default([]) | map(attribute='name') | list }}"
|
||||
_yaml_dns_names: "{{ netbird_dns_nameserver_groups | default([]) | map(attribute='name') | list }}"
|
||||
_yaml_dns_zone_names: "{{ netbird_dns_zones | default([]) | map(attribute='name') | list }}"
|
||||
_yaml_net_names: "{{ netbird_networks | default([]) | map(attribute='name') | list }}"
|
||||
_yaml_pol_names: "{{ netbird_policies | default([]) | map(attribute='name') | list }}"
|
||||
when: commit | bool and strict | bool
|
||||
|
||||
- name: "Strict: Identify unmanaged resources"
|
||||
ansible.builtin.set_fact:
|
||||
_orphaned_policies: "{{ api_policies.data | map(attribute='name') | list | difference(_yaml_pol_names) }}"
|
||||
_orphaned_networks: "{{ api_networks.data | map(attribute='name') | list | difference(_yaml_net_names) }}"
|
||||
_orphaned_dns: "{{ api_dns_nameservers.data | map(attribute='name') | list | difference(_yaml_dns_names) }}"
|
||||
_orphaned_dns_zones: "{{ api_dns_zones.data | map(attribute='name') | list | difference(_yaml_dns_zone_names) }}"
|
||||
_orphaned_posture_checks: "{{ api_posture_checks.data | map(attribute='name') | list | difference(_yaml_pc_names) }}"
|
||||
_orphaned_groups: "{{ api_groups.data | map(attribute='name') | list | difference(_yaml_group_names) | difference(_protected_groups) }}"
|
||||
when: commit | bool and strict | bool
|
||||
|
||||
- name: "Strict: Display resources to be removed"
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "Strict mode — removing unmanaged resources:"
|
||||
- " Policies: {{ _orphaned_policies | length }} ({{ _orphaned_policies | join(', ') }})"
|
||||
- " Networks: {{ _orphaned_networks | length }} ({{ _orphaned_networks | join(', ') }})"
|
||||
- " DNS nameservers: {{ _orphaned_dns | length }} ({{ _orphaned_dns | join(', ') }})"
|
||||
- " DNS zones: {{ _orphaned_dns_zones | length }} ({{ _orphaned_dns_zones | join(', ') }})"
|
||||
- " Posture checks: {{ _orphaned_posture_checks | length }} ({{ _orphaned_posture_checks | join(', ') }})"
|
||||
- " Groups: {{ _orphaned_groups | length }} ({{ _orphaned_groups | join(', ') }})"
|
||||
when: commit | bool and strict | bool
|
||||
|
||||
- name: "Strict: Remove unmanaged policies"
|
||||
community.ansible_netbird.netbird_policy:
|
||||
name: "{{ item }}"
|
||||
state: absent
|
||||
loop: "{{ _orphaned_policies }}"
|
||||
loop_control:
|
||||
label: "{{ item }}"
|
||||
when: commit | bool and strict | bool and _orphaned_policies | length > 0
|
||||
|
||||
- name: "Strict: Remove unmanaged networks"
|
||||
community.ansible_netbird.netbird_network:
|
||||
name: "{{ item }}"
|
||||
state: absent
|
||||
loop: "{{ _orphaned_networks }}"
|
||||
loop_control:
|
||||
label: "{{ item }}"
|
||||
when: commit | bool and strict | bool and _orphaned_networks | length > 0
|
||||
|
||||
- name: "Strict: Remove unmanaged DNS nameserver groups"
|
||||
community.ansible_netbird.netbird_dns:
|
||||
resource_type: nameserver_group
|
||||
name: "{{ item }}"
|
||||
state: absent
|
||||
loop: "{{ _orphaned_dns }}"
|
||||
loop_control:
|
||||
label: "{{ item }}"
|
||||
when: commit | bool and strict | bool and _orphaned_dns | length > 0
|
||||
|
||||
- name: "Strict: Remove unmanaged DNS zones"
|
||||
community.ansible_netbird.netbird_dns_zone:
|
||||
name: "{{ item }}"
|
||||
state: absent
|
||||
loop: "{{ _orphaned_dns_zones }}"
|
||||
loop_control:
|
||||
label: "{{ item }}"
|
||||
when: commit | bool and strict | bool and _orphaned_dns_zones | length > 0
|
||||
|
||||
- name: "Strict: Remove unmanaged posture checks"
|
||||
community.ansible_netbird.netbird_posture_check:
|
||||
name: "{{ item }}"
|
||||
state: absent
|
||||
loop: "{{ _orphaned_posture_checks }}"
|
||||
loop_control:
|
||||
label: "{{ item }}"
|
||||
when: commit | bool and strict | bool and _orphaned_posture_checks | length > 0
|
||||
|
||||
- name: "Strict: Remove unmanaged groups"
|
||||
community.ansible_netbird.netbird_group:
|
||||
name: "{{ item }}"
|
||||
state: absent
|
||||
loop: "{{ _orphaned_groups }}"
|
||||
loop_control:
|
||||
label: "{{ item }}"
|
||||
when: commit | bool and strict | bool and _orphaned_groups | length > 0
|
||||
|
||||
# =====================================================================
|
||||
# Summary
|
||||
# =====================================================================
|
||||
- name: Configuration complete
|
||||
ansible.builtin.debug:
|
||||
msg: "NetBird configuration applied successfully from {{ config_dir }}/"
|
||||
when: commit | bool
|
||||
|
||||
module_defaults:
|
||||
group/community.ansible_netbird.netbird:
|
||||
api_url: "{{ netbird_api_url }}"
|
||||
api_token: "{{ netbird_api_token }}"
|
||||
validate_certs: "{{ netbird_validate_certs }}"
|
||||
+2
-6
@@ -1,10 +1,10 @@
|
||||
---
|
||||
# tasks/preview_diff_report.yml — Compute and display configuration diff report
|
||||
#
|
||||
# Included by configure_netbird.yml when commit=false (default).
|
||||
# Included by the configure role when commit=false (default).
|
||||
# Shows which resources will be added, removed, or re-applied.
|
||||
#
|
||||
# Expects these variables to be set by the parent playbook:
|
||||
# Expects these variables to be set by the parent role:
|
||||
# - api_groups, api_posture_checks, api_dns_nameservers, api_dns_zones,
|
||||
# api_networks, api_policies, api_accounts (registered from netbird_info)
|
||||
# - netbird_groups, netbird_posture_checks, netbird_dns_nameserver_groups,
|
||||
@@ -38,10 +38,6 @@
|
||||
# Classify resources: new / existing / remove
|
||||
# =========================================================================
|
||||
|
||||
# Helper: extract present/absent names from a resource list
|
||||
# "present" = no state defined OR state == present
|
||||
# "absent" = state == absent
|
||||
|
||||
- name: Classify groups
|
||||
ansible.builtin.set_fact:
|
||||
groups_new: >-
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
# Output directory for exported config files
|
||||
export_dir: "/tmp/netbird_config_export"
|
||||
|
||||
# Validate API TLS certificates
|
||||
netbird_validate_certs: true
|
||||
@@ -0,0 +1,375 @@
|
||||
---
|
||||
# Role: community.ansible_netbird.export
|
||||
#
|
||||
# Captures the current NetBird API state and produces clean, ready-to-use
|
||||
# config files that can be used directly with the configure role/playbook.
|
||||
# Also saves raw API data in a raw/ subdirectory for debugging.
|
||||
#
|
||||
# Required variables:
|
||||
# - netbird_api_url: NetBird API URL (e.g., https://netbird.example.com)
|
||||
# - netbird_api_token: NetBird API token
|
||||
#
|
||||
# Optional variables:
|
||||
# - export_dir: Output directory (default: /tmp/netbird_config_export)
|
||||
# - netbird_validate_certs: Validate API TLS certs (default: true)
|
||||
#
|
||||
# Note: This role uses ansible_date_time — ensure gather_facts is enabled
|
||||
# in your playbook (gather_facts: true).
|
||||
|
||||
# =========================================================================
|
||||
# Validation
|
||||
# =========================================================================
|
||||
- name: Validate API configuration
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- netbird_api_url is defined
|
||||
- netbird_api_url | length > 0
|
||||
- netbird_api_token is defined
|
||||
- netbird_api_token | length > 0
|
||||
fail_msg: |
|
||||
NetBird API configuration is missing!
|
||||
Required variables:
|
||||
netbird_api_url: "https://netbird.example.com"
|
||||
netbird_api_token: "your-api-token"
|
||||
|
||||
- name: Gather facts if needed
|
||||
ansible.builtin.setup:
|
||||
when: ansible_date_time is not defined
|
||||
|
||||
- name: Create export directories
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
loop:
|
||||
- "{{ export_dir }}"
|
||||
- "{{ export_dir }}/access_control"
|
||||
- "{{ export_dir }}/dns"
|
||||
- "{{ export_dir }}/raw"
|
||||
|
||||
# =========================================================================
|
||||
# All API-calling tasks wrapped in a block with module_defaults
|
||||
# =========================================================================
|
||||
- name: Export NetBird configuration
|
||||
block:
|
||||
# =====================================================================
|
||||
# Fetch all resources from the NetBird API
|
||||
# =====================================================================
|
||||
- name: Fetch account settings
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: accounts
|
||||
register: accounts_data
|
||||
|
||||
- name: Fetch groups
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: groups
|
||||
register: groups_data
|
||||
|
||||
- name: Fetch posture checks
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: posture_checks
|
||||
register: posture_checks_data
|
||||
|
||||
- name: Fetch setup keys
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: setup_keys
|
||||
register: setup_keys_data
|
||||
|
||||
- name: Fetch DNS nameserver groups
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: dns_nameservers
|
||||
register: dns_nameservers_data
|
||||
|
||||
- name: Fetch DNS settings
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: dns_settings
|
||||
register: dns_settings_data
|
||||
|
||||
- name: Fetch DNS zones
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: dns_zones
|
||||
register: dns_zones_data
|
||||
|
||||
- name: Fetch routes (deprecated API)
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: routes
|
||||
register: routes_data
|
||||
|
||||
- name: Fetch networks
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: networks
|
||||
register: networks_data
|
||||
|
||||
- name: Fetch routers for each network
|
||||
ansible.builtin.uri:
|
||||
url: "{{ netbird_api_url }}/api/networks/{{ item.id }}/routers"
|
||||
method: GET
|
||||
headers:
|
||||
Authorization: "Token {{ netbird_api_token }}"
|
||||
Accept: "application/json"
|
||||
validate_certs: "{{ netbird_validate_certs }}"
|
||||
return_content: true
|
||||
loop: "{{ networks_data.data }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
register: networks_routers_raw
|
||||
when: networks_data.data | length > 0
|
||||
|
||||
- name: Fetch resources for each network
|
||||
ansible.builtin.uri:
|
||||
url: "{{ netbird_api_url }}/api/networks/{{ item.id }}/resources"
|
||||
method: GET
|
||||
headers:
|
||||
Authorization: "Token {{ netbird_api_token }}"
|
||||
Accept: "application/json"
|
||||
validate_certs: "{{ netbird_validate_certs }}"
|
||||
return_content: true
|
||||
loop: "{{ networks_data.data }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
register: networks_resources_raw
|
||||
when: networks_data.data | length > 0
|
||||
|
||||
- name: Enrich networks with routers and resources
|
||||
ansible.builtin.set_fact:
|
||||
networks_enriched: >-
|
||||
{% set result = [] -%}
|
||||
{% for net in networks_data.data -%}
|
||||
{% set routers = networks_routers_raw.results[loop.index0].get('json', []) -%}
|
||||
{% set resources = networks_resources_raw.results[loop.index0].get('json', []) -%}
|
||||
{% set _ = result.append(net | combine({'routers': routers, 'resources': resources})) -%}
|
||||
{% endfor -%}
|
||||
{{ result | to_json }}
|
||||
when: networks_data.data | length > 0
|
||||
|
||||
- name: Set empty enriched networks when none exist
|
||||
ansible.builtin.set_fact:
|
||||
networks_enriched: []
|
||||
when: networks_data.data | length == 0
|
||||
|
||||
- name: Fetch policies
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: policies
|
||||
register: policies_data
|
||||
|
||||
- name: Fetch users
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: users
|
||||
register: users_data
|
||||
|
||||
- name: Fetch peers
|
||||
community.ansible_netbird.netbird_info:
|
||||
resource: peers
|
||||
register: peers_data
|
||||
|
||||
# =====================================================================
|
||||
# Build ID-to-name lookup maps
|
||||
# =====================================================================
|
||||
- name: Initialize lookup maps
|
||||
ansible.builtin.set_fact:
|
||||
group_id_map: {}
|
||||
posture_check_id_map: {}
|
||||
peer_id_map: {}
|
||||
|
||||
- name: Build group ID-to-name map
|
||||
ansible.builtin.set_fact:
|
||||
group_id_map: "{{ group_id_map | combine({item.id: item.name}) }}"
|
||||
loop: "{{ groups_data.data }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
when: groups_data.data | length > 0
|
||||
|
||||
- name: Build posture check ID-to-name map
|
||||
ansible.builtin.set_fact:
|
||||
posture_check_id_map: "{{ posture_check_id_map | combine({item.id: item.name}) }}"
|
||||
loop: "{{ posture_checks_data.data }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
when: posture_checks_data.data | length > 0
|
||||
|
||||
- name: Build peer ID-to-name map
|
||||
ansible.builtin.set_fact:
|
||||
peer_id_map: "{{ peer_id_map | combine({item.id: item.name}) }}"
|
||||
loop: "{{ peers_data.data }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
when: peers_data.data | length > 0
|
||||
|
||||
# =====================================================================
|
||||
# Write clean config files (ready to use with configure role)
|
||||
# =====================================================================
|
||||
- name: Export clean settings
|
||||
ansible.builtin.template:
|
||||
src: export/settings.yml.j2
|
||||
dest: "{{ export_dir }}/settings.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export clean groups
|
||||
ansible.builtin.template:
|
||||
src: export/access_control/groups.yml.j2
|
||||
dest: "{{ export_dir }}/access_control/groups.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export clean posture checks
|
||||
ansible.builtin.template:
|
||||
src: export/access_control/posture_checks.yml.j2
|
||||
dest: "{{ export_dir }}/access_control/posture_checks.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export clean policies
|
||||
ansible.builtin.template:
|
||||
src: export/access_control/policies.yml.j2
|
||||
dest: "{{ export_dir }}/access_control/policies.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export clean setup keys
|
||||
ansible.builtin.template:
|
||||
src: export/setup_keys.yml.j2
|
||||
dest: "{{ export_dir }}/setup_keys.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export clean DNS nameservers
|
||||
ansible.builtin.template:
|
||||
src: export/dns/nameservers.yml.j2
|
||||
dest: "{{ export_dir }}/dns/nameservers.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export clean DNS zones
|
||||
ansible.builtin.template:
|
||||
src: export/dns/zones.yml.j2
|
||||
dest: "{{ export_dir }}/dns/zones.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export clean DNS settings
|
||||
ansible.builtin.template:
|
||||
src: export/dns/settings.yml.j2
|
||||
dest: "{{ export_dir }}/dns/settings.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export clean networks
|
||||
ansible.builtin.template:
|
||||
src: export/networks.yml.j2
|
||||
dest: "{{ export_dir }}/networks.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export clean routes (deprecated API)
|
||||
ansible.builtin.template:
|
||||
src: export/routes.yml.j2
|
||||
dest: "{{ export_dir }}/routes.yml"
|
||||
mode: "0644"
|
||||
|
||||
# =====================================================================
|
||||
# Write raw API data (for debugging/reference)
|
||||
# =====================================================================
|
||||
- name: Export raw account settings
|
||||
ansible.builtin.copy:
|
||||
content: "{{ accounts_data.data | to_nice_yaml(indent=2) }}"
|
||||
dest: "{{ export_dir }}/raw/account_settings_raw.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export raw groups
|
||||
ansible.builtin.copy:
|
||||
content: "{{ groups_data.data | to_nice_yaml(indent=2) }}"
|
||||
dest: "{{ export_dir }}/raw/groups_raw.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export raw posture checks
|
||||
ansible.builtin.copy:
|
||||
content: "{{ posture_checks_data.data | to_nice_yaml(indent=2) }}"
|
||||
dest: "{{ export_dir }}/raw/posture_checks_raw.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export raw setup keys
|
||||
ansible.builtin.copy:
|
||||
content: "{{ setup_keys_data.data | to_nice_yaml(indent=2) }}"
|
||||
dest: "{{ export_dir }}/raw/setup_keys_raw.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export raw DNS
|
||||
ansible.builtin.copy:
|
||||
content: "{{ dns_nameservers_data.data | to_nice_yaml(indent=2) }}"
|
||||
dest: "{{ export_dir }}/raw/dns_nameservers_raw.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export raw DNS zones
|
||||
ansible.builtin.copy:
|
||||
content: "{{ dns_zones_data.data | to_nice_yaml(indent=2) }}"
|
||||
dest: "{{ export_dir }}/raw/dns_zones_raw.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export raw networks
|
||||
ansible.builtin.copy:
|
||||
content: "{{ networks_data.data | to_nice_yaml(indent=2) }}"
|
||||
dest: "{{ export_dir }}/raw/networks_raw.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export raw routes
|
||||
ansible.builtin.copy:
|
||||
content: "{{ routes_data.data | to_nice_yaml(indent=2) }}"
|
||||
dest: "{{ export_dir }}/raw/routes_raw.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export raw policies
|
||||
ansible.builtin.copy:
|
||||
content: "{{ policies_data.data | to_nice_yaml(indent=2) }}"
|
||||
dest: "{{ export_dir }}/raw/policies_raw.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export users reference
|
||||
ansible.builtin.copy:
|
||||
content: |
|
||||
---
|
||||
# NetBird Users Reference ({{ users_data.data | length }} users)
|
||||
# Exported on {{ ansible_date_time.date }}
|
||||
# Users are managed via IdP sync, not via config-as-code.
|
||||
{{ users_data.data | to_nice_yaml(indent=2) }}
|
||||
dest: "{{ export_dir }}/raw/users_reference.yml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Export peers reference
|
||||
ansible.builtin.copy:
|
||||
content: |
|
||||
---
|
||||
# NetBird Peers Reference ({{ peers_data.data | length }} peers)
|
||||
# Exported on {{ ansible_date_time.date }}
|
||||
# Peers register via setup keys — not managed via config-as-code.
|
||||
# Use peer IDs from this file for network router configuration.
|
||||
{{ peers_data.data | to_nice_yaml(indent=2) }}
|
||||
dest: "{{ export_dir }}/raw/peers_reference.yml"
|
||||
mode: "0644"
|
||||
|
||||
# =====================================================================
|
||||
# Summary
|
||||
# =====================================================================
|
||||
- name: Display export summary
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "Export complete! Files written to: {{ export_dir }}/"
|
||||
- ""
|
||||
- "Clean config files (ready to use with configure role):"
|
||||
- " - settings.yml (account settings)"
|
||||
- " - access_control/"
|
||||
- " groups.yml ({{ groups_data.data | rejectattr('name', 'equalto', 'All') | rejectattr('issued', 'equalto', 'jwt') | list | length }} groups)"
|
||||
- " posture_checks.yml ({{ posture_checks_data.data | length }} posture checks)"
|
||||
- " policies.yml ({{ policies_data.data | length }} policies)"
|
||||
- " - setup_keys.yml ({{ setup_keys_data.data | length }} setup keys — read-only reference)"
|
||||
- " - dns/"
|
||||
- " nameservers.yml ({{ dns_nameservers_data.data | length }} nameserver groups)"
|
||||
- " zones.yml ({{ dns_zones_data.data | length }} zones)"
|
||||
- " settings.yml (DNS settings)"
|
||||
- " - networks.yml ({{ networks_data.data | length }} networks)"
|
||||
- " - routes.yml ({{ routes_data.data | length }} routes — deprecated API)"
|
||||
- ""
|
||||
- "Raw API data (for debugging):"
|
||||
- " raw/ (account, groups, posture_checks, setup_keys, dns, routes, networks, policies, users, peers)"
|
||||
- ""
|
||||
- "Next steps:"
|
||||
- " 1. Review the clean config files"
|
||||
- " 2. Copy them to your config directory (preserving directory structure)"
|
||||
- " 3. Run the configure role/playbook to preview"
|
||||
|
||||
module_defaults:
|
||||
group/community.ansible_netbird.netbird:
|
||||
api_url: "{{ netbird_api_url }}"
|
||||
api_token: "{{ netbird_api_token }}"
|
||||
validate_certs: "{{ netbird_validate_certs }}"
|
||||
Reference in New Issue
Block a user