--- # ============================================================================= # NetBird v1.6 - Instance Bootstrap # ============================================================================= # Run ONCE on fresh deployment to: # 1. Check if instance needs setup # 2. Create initial admin user via API # 3. Display instructions for manual PAT creation # # NOTE: The embedded IdP (Dex) does not support password grants, # so PATs cannot be created programmatically without dashboard access. # After this playbook, you MUST manually: # 1. Login to dashboard with admin credentials # 2. Create a service user # 3. Generate PAT for service user # 4. Store PAT in vault.yml as vault_netbird_service_pat # # Run: # ansible-playbook -i inventory.yml setup-bootstrap.yml --ask-vault-pass # ============================================================================= - name: Bootstrap NetBird Instance hosts: netbird_servers become: false gather_facts: true vars_files: - group_vars/netbird_servers.yml - group_vars/vault.yml vars: # For SSL-IP mode, use server IP; for domain mode, use netbird_domain netbird_api_host: "{{ hostvars[inventory_hostname].ansible_host | default(netbird_domain) }}" netbird_api_url: "https://{{ netbird_api_host }}/api" tasks: # ========================================================================= # Check Instance Status # ========================================================================= - name: Check instance status ansible.builtin.uri: url: "{{ netbird_api_url }}/instance" method: GET validate_certs: false status_code: [200, 404] register: instance_status delegate_to: localhost ignore_errors: true - name: Debug instance status ansible.builtin.debug: msg: "Instance API response: status={{ instance_status.status | default('N/A') }}, json={{ instance_status.json | default('N/A') }}" - name: Determine setup status ansible.builtin.set_fact: setup_required: >- {{ instance_status.status != 200 or (instance_status.json.setup_required | default(false)) }} instance_check_failed: "{{ instance_status.status != 200 }}" - name: Check PAT status ansible.builtin.set_fact: pat_configured: "{{ vault_netbird_service_pat is defined and vault_netbird_service_pat | length > 0 }}" - name: Display status - already configured ansible.builtin.debug: msg: | ============================================ Instance Already Configured ============================================ {% if pat_configured %} Service PAT: ✓ Configured Ready to provision users: ansible-playbook -i inventory.yml setup-groups.yml --ask-vault-pass ansible-playbook -i inventory.yml setup-users.yml --ask-vault-pass {% else %} Service PAT: ✗ Not configured NEXT STEPS: 1. Login to dashboard: https://{{ netbird_api_host }} (Accept self-signed certificate warning) 2. Create service user: Team → Service Users → Create 3. Generate PAT: Select user → Create Token 4. Store in vault: ansible-vault edit group_vars/vault.yml Set: vault_netbird_service_pat: "" 5. Then run: ansible-playbook -i inventory.yml setup-groups.yml --ask-vault-pass ansible-playbook -i inventory.yml setup-users.yml --ask-vault-pass {% endif %} ============================================ when: not setup_required - name: End play - instance already configured ansible.builtin.meta: end_play when: not setup_required # ========================================================================= # Bootstrap Admin User # ========================================================================= - name: Attempt to create initial admin user ansible.builtin.uri: url: "{{ netbird_api_url }}/instance/setup" method: POST headers: Content-Type: "application/json" body_format: json body: email: "{{ netbird_admin_user.email }}" password: "{{ netbird_admin_user.password }}" name: "{{ netbird_admin_user.name }}" validate_certs: false status_code: [200, 201, 404] register: bootstrap_result delegate_to: localhost - name: Handle 404 - API endpoint not available ansible.builtin.debug: msg: | ============================================ Bootstrap API Not Available (404) ============================================ The /api/instance/setup endpoint returned 404. This means either: - Admin already exists, OR - This endpoint isn't exposed in your NetBird version Create admin manually in the dashboard: 1. Go to: https://{{ netbird_api_host }} 2. Create admin account: Email: {{ netbird_admin_user.email }} Password: {{ netbird_admin_user.password }} Then create service user + PAT: 3. Go to Team → Service Users → Create 4. Generate PAT: Select user → Create Token 5. Store in vault: ansible-vault edit group_vars/vault.yml Set: vault_netbird_service_pat: "" Then provision users: ansible-playbook -i inventory.yml setup-groups.yml --ask-vault-pass ansible-playbook -i inventory.yml setup-users.yml --ask-vault-pass ============================================ when: bootstrap_result.status == 404 - name: End play if already bootstrapped ansible.builtin.meta: end_play when: bootstrap_result.status == 404 - name: Display bootstrap result ansible.builtin.debug: msg: | ============================================ NetBird Instance Bootstrapped! ============================================ Admin User Created: Email: {{ netbird_admin_user.email }} Password: {{ netbird_admin_user.password }} User ID: {{ bootstrap_result.json.user_id | default('N/A') }} ============================================ MANUAL STEPS REQUIRED: ============================================ 1. Login to dashboard: https://{{ netbird_api_host }} (Accept self-signed certificate warning) 2. Login with: Email: {{ netbird_admin_user.email }} Password: {{ netbird_admin_user.password }} 3. Create Service User: - Go to Team → Service Users - Click "Create Service User" - Name: "Automation Service" - Role: "Admin" - Save the user 4. Create PAT for Service User: - Select the service user - Click "Create Token" - Name: "ansible-automation" - Expiration: 365 days - COPY THE TOKEN (shown only once!) 5. Store PAT in vault: ansible-vault edit group_vars/vault.yml Set: vault_netbird_service_pat: "" 6. Run group and user provisioning: ansible-playbook -i inventory.yml setup-groups.yml --ask-vault-pass ansible-playbook -i inventory.yml setup-users.yml --ask-vault-pass ============================================ when: setup_required | default(true)