You've already forked ansible-netbird
mirror of
https://github.com/netbirdio/ansible-netbird.git
synced 2026-05-22 18:43:36 -07:00
2041cd2d89
Changes module behavior so that omitting auto_groups, peers, or similar list fields on update preserves the existing values instead of wiping them to []. Modules changed: - netbird_setup_key: auto_groups default [] -> None, preserve on update - netbird_group: peers default [] -> None, preserve on update, normalize peer dicts to IDs - netbird_user: auto_groups default [] -> None, preserve on update Also: - Reimplement get_current_user() since /api/users/me does not exist. Now lists users and matches by is_current flag, with explicit error on multi-user deployments without the flag. - Configure role: use default(omit) for setup key auto_groups so the module-level preservation works through the role. - Configure role: conditional auto_groups resolution (skip when auto_groups not defined in YAML config). - Configure role: DNS zone distribution_groups now handles both group names and raw IDs (falls back to original value when not found in group_ids map). - Updated DOCUMENTATION strings to remove stale default: [] and document preservation behavior.
329 lines
9.5 KiB
Python
329 lines
9.5 KiB
Python
#!/usr/bin/python
|
|
# -*- coding: utf-8 -*-
|
|
# Copyright: (c) 2024, Community
|
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
|
|
"""Ansible module for managing NetBird setup keys."""
|
|
|
|
from __future__ import absolute_import, division, print_function
|
|
__metaclass__ = type
|
|
|
|
DOCUMENTATION = r'''
|
|
---
|
|
module: netbird_setup_key
|
|
short_description: Manage NetBird setup keys
|
|
description:
|
|
- Create, update, and delete setup keys in NetBird.
|
|
- Setup keys are used to register new peers to the network.
|
|
version_added: "1.0.0"
|
|
author:
|
|
- Community
|
|
options:
|
|
state:
|
|
description:
|
|
- The desired state of the setup key.
|
|
type: str
|
|
choices: ['present', 'absent']
|
|
default: present
|
|
key_id:
|
|
description:
|
|
- The unique identifier of the setup key.
|
|
- Required when state is absent or when updating by ID.
|
|
type: str
|
|
name:
|
|
description:
|
|
- Name of the setup key.
|
|
- Required when creating a new setup key.
|
|
type: str
|
|
key_type:
|
|
description:
|
|
- Type of the setup key.
|
|
- 'one-off' keys can only be used once.
|
|
- 'reusable' keys can be used multiple times.
|
|
type: str
|
|
choices: ['one-off', 'reusable']
|
|
default: one-off
|
|
expires_in:
|
|
description:
|
|
- Expiration time in seconds.
|
|
- Default is 86400 (24 hours).
|
|
type: int
|
|
default: 86400
|
|
revoked:
|
|
description:
|
|
- Whether the key is revoked.
|
|
type: bool
|
|
default: false
|
|
auto_groups:
|
|
description:
|
|
- List of group IDs to auto-assign to peers registered with this key.
|
|
- When updating an existing key and this is not specified, the current auto_groups are preserved.
|
|
type: list
|
|
elements: str
|
|
usage_limit:
|
|
description:
|
|
- Maximum number of times the key can be used.
|
|
- 0 means unlimited (for reusable keys).
|
|
type: int
|
|
default: 0
|
|
ephemeral:
|
|
description:
|
|
- Whether peers registered with this key are ephemeral.
|
|
- Ephemeral peers are automatically removed when disconnected.
|
|
type: bool
|
|
default: false
|
|
allow_extra_dns_labels:
|
|
description:
|
|
- Allow extra DNS labels for peers registered with this key.
|
|
type: bool
|
|
default: false
|
|
extends_documentation_fragment:
|
|
- community.ansible_netbird.netbird
|
|
requirements:
|
|
- python >= 3.6
|
|
'''
|
|
|
|
EXAMPLES = r'''
|
|
- name: Create a one-off setup key
|
|
community.ansible_netbird.netbird_setup_key:
|
|
api_url: "https://netbird.example.com"
|
|
api_token: "{{ netbird_token }}"
|
|
name: "new-server-key"
|
|
key_type: "one-off"
|
|
expires_in: 3600
|
|
state: present
|
|
register: setup_key
|
|
|
|
- name: Create a reusable setup key with auto groups
|
|
community.ansible_netbird.netbird_setup_key:
|
|
api_url: "https://netbird.example.com"
|
|
api_token: "{{ netbird_token }}"
|
|
name: "developer-machines"
|
|
key_type: "reusable"
|
|
expires_in: 604800
|
|
auto_groups:
|
|
- "developers-group-id"
|
|
state: present
|
|
|
|
- name: Create an ephemeral setup key
|
|
community.ansible_netbird.netbird_setup_key:
|
|
api_url: "https://netbird.example.com"
|
|
api_token: "{{ netbird_token }}"
|
|
name: "temporary-access"
|
|
key_type: "reusable"
|
|
ephemeral: true
|
|
state: present
|
|
|
|
- name: Revoke a setup key
|
|
community.ansible_netbird.netbird_setup_key:
|
|
api_url: "https://netbird.example.com"
|
|
api_token: "{{ netbird_token }}"
|
|
key_id: "key-id-123"
|
|
revoked: true
|
|
state: present
|
|
|
|
- name: Delete a setup key
|
|
community.ansible_netbird.netbird_setup_key:
|
|
api_url: "https://netbird.example.com"
|
|
api_token: "{{ netbird_token }}"
|
|
key_id: "key-id-123"
|
|
state: absent
|
|
'''
|
|
|
|
RETURN = r'''
|
|
setup_key:
|
|
description: The setup key object.
|
|
returned: success
|
|
type: dict
|
|
contains:
|
|
id:
|
|
description: Setup key ID.
|
|
type: str
|
|
key:
|
|
description: The actual setup key value (only returned on creation).
|
|
type: str
|
|
name:
|
|
description: Setup key name.
|
|
type: str
|
|
type:
|
|
description: Key type (one-off or reusable).
|
|
type: str
|
|
expires:
|
|
description: Expiration timestamp.
|
|
type: str
|
|
revoked:
|
|
description: Whether the key is revoked.
|
|
type: bool
|
|
auto_groups:
|
|
description: Auto-assigned group IDs.
|
|
type: list
|
|
usage_limit:
|
|
description: Usage limit.
|
|
type: int
|
|
used_times:
|
|
description: Number of times the key has been used.
|
|
type: int
|
|
last_used:
|
|
description: Last used timestamp.
|
|
type: str
|
|
ephemeral:
|
|
description: Whether key creates ephemeral peers.
|
|
type: bool
|
|
valid:
|
|
description: Whether the key is still valid.
|
|
type: bool
|
|
'''
|
|
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
from ansible_collections.community.ansible_netbird.plugins.module_utils.netbird_api import (
|
|
NetBirdAPI,
|
|
NetBirdAPIError,
|
|
extract_ids,
|
|
netbird_argument_spec
|
|
)
|
|
|
|
|
|
def find_setup_key_by_name(api, name):
|
|
"""Find a setup key by name."""
|
|
keys, _ = api.list_setup_keys()
|
|
for key in (keys or []):
|
|
if key.get('name') == name:
|
|
return key
|
|
return None
|
|
|
|
|
|
def setup_key_needs_update(current, params):
|
|
"""Check if setup key needs to be updated."""
|
|
if params.get('name') is not None and current.get('name') != params['name']:
|
|
return True
|
|
if params.get('revoked') is not None and current.get('revoked') != params['revoked']:
|
|
return True
|
|
if params.get('auto_groups') is not None:
|
|
current_groups = set(extract_ids(current.get('auto_groups') or []))
|
|
desired_groups = set(extract_ids(params['auto_groups'] or []))
|
|
if current_groups != desired_groups:
|
|
return True
|
|
return False
|
|
|
|
|
|
def run_module():
|
|
"""Main module execution."""
|
|
argument_spec = netbird_argument_spec()
|
|
argument_spec.update(
|
|
state=dict(type='str', choices=['present', 'absent'], default='present'),
|
|
key_id=dict(type='str'),
|
|
name=dict(type='str'),
|
|
key_type=dict(type='str', choices=['one-off', 'reusable'], default='one-off'),
|
|
expires_in=dict(type='int', default=86400),
|
|
revoked=dict(type='bool', default=False),
|
|
auto_groups=dict(type='list', elements='str'),
|
|
usage_limit=dict(type='int', default=0),
|
|
ephemeral=dict(type='bool', default=False),
|
|
allow_extra_dns_labels=dict(type='bool', default=False)
|
|
)
|
|
|
|
module = AnsibleModule(
|
|
argument_spec=argument_spec,
|
|
supports_check_mode=True,
|
|
required_one_of=[
|
|
('key_id', 'name'),
|
|
]
|
|
)
|
|
|
|
api = NetBirdAPI(
|
|
module,
|
|
module.params['api_url'],
|
|
module.params['api_token'],
|
|
module.params['validate_certs']
|
|
)
|
|
|
|
state = module.params['state']
|
|
key_id = module.params['key_id']
|
|
name = module.params['name']
|
|
|
|
result = dict(
|
|
changed=False,
|
|
setup_key={}
|
|
)
|
|
|
|
try:
|
|
# Find existing setup key
|
|
existing_key = None
|
|
if key_id:
|
|
try:
|
|
existing_key, _ = api.get_setup_key(key_id)
|
|
except NetBirdAPIError as e:
|
|
if e.status_code != 404:
|
|
raise
|
|
elif name:
|
|
existing_key = find_setup_key_by_name(api, name)
|
|
|
|
if state == 'absent':
|
|
if existing_key:
|
|
if not module.check_mode:
|
|
api.delete_setup_key(existing_key['id'])
|
|
result['changed'] = True
|
|
result['msg'] = 'Setup key deleted successfully'
|
|
module.exit_json(**result)
|
|
|
|
# state == 'present'
|
|
if existing_key:
|
|
# Use existing values as fallback for fields the user didn't specify
|
|
effective_auto_groups = module.params['auto_groups']
|
|
if effective_auto_groups is None:
|
|
effective_auto_groups = existing_key.get('auto_groups', [])
|
|
|
|
# Check if update is needed
|
|
update_params = {
|
|
'name': name,
|
|
'revoked': module.params['revoked'],
|
|
'auto_groups': effective_auto_groups
|
|
}
|
|
|
|
if setup_key_needs_update(existing_key, update_params):
|
|
if not module.check_mode:
|
|
key, _ = api.update_setup_key(
|
|
existing_key['id'],
|
|
revoked=module.params['revoked'],
|
|
auto_groups=effective_auto_groups
|
|
)
|
|
result['setup_key'] = key
|
|
else:
|
|
result['setup_key'] = existing_key
|
|
result['changed'] = True
|
|
else:
|
|
result['setup_key'] = existing_key
|
|
else:
|
|
# Create new setup key
|
|
if not name:
|
|
module.fail_json(msg="name is required when creating a new setup key")
|
|
|
|
if not module.check_mode:
|
|
key, _ = api.create_setup_key(
|
|
name=name,
|
|
key_type=module.params['key_type'],
|
|
expires_in=module.params['expires_in'],
|
|
revoked=module.params['revoked'],
|
|
auto_groups=module.params['auto_groups'] or [],
|
|
usage_limit=module.params['usage_limit'],
|
|
ephemeral=module.params['ephemeral'],
|
|
allow_extra_dns_labels=module.params['allow_extra_dns_labels']
|
|
)
|
|
result['setup_key'] = key
|
|
result['changed'] = True
|
|
|
|
module.exit_json(**result)
|
|
|
|
except NetBirdAPIError as e:
|
|
module.fail_json(msg=str(e), status_code=e.status_code, response=e.response)
|
|
|
|
|
|
def main():
|
|
run_module()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|
|
|
|
|