From 1402b81b647c973c914c69b28245d3ea4d00e4c9 Mon Sep 17 00:00:00 2001 From: Tomas Kloda Date: Fri, 27 Mar 2026 10:59:57 +0100 Subject: [PATCH] module id fixes --- plugins/module_utils/netbird_api.py | 13 +++++++++ plugins/modules/netbird_dns.py | 9 +++--- plugins/modules/netbird_dns_zone.py | 5 ++-- plugins/modules/netbird_group.py | 11 ++------ plugins/modules/netbird_network.py | 7 +++-- plugins/modules/netbird_policy.py | 42 +++++++++++++++++++++++++--- plugins/modules/netbird_route.py | 11 ++++---- plugins/modules/netbird_setup_key.py | 5 ++-- plugins/modules/netbird_user.py | 5 ++-- 9 files changed, 78 insertions(+), 30 deletions(-) diff --git a/plugins/module_utils/netbird_api.py b/plugins/module_utils/netbird_api.py index bd7dcf7..13898ec 100644 --- a/plugins/module_utils/netbird_api.py +++ b/plugins/module_utils/netbird_api.py @@ -14,6 +14,19 @@ from ansible.module_utils.basic import env_fallback from ansible.module_utils.six.moves.urllib.error import HTTPError, URLError +def extract_ids(items): + """Extract IDs from a list that may contain dicts or plain strings. + + The NetBird API returns related objects as dicts (e.g. + ``[{"id": "abc", "name": "..."}]``) while module parameters are plain + ID strings. This helper normalises both forms to a flat list of ID + strings so they can be safely compared with ``set()``. + """ + if not items: + return [] + return [item['id'] if isinstance(item, dict) else item for item in items] + + class NetBirdAPIError(Exception): """Exception raised for NetBird API errors.""" def __init__(self, message, status_code=None, response=None): diff --git a/plugins/modules/netbird_dns.py b/plugins/modules/netbird_dns.py index df5574b..c8cd940 100644 --- a/plugins/modules/netbird_dns.py +++ b/plugins/modules/netbird_dns.py @@ -213,6 +213,7 @@ 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 ) @@ -239,8 +240,8 @@ def nsgroup_needs_update(current, params): # Check groups if params.get('groups') is not None: - current_groups = set(current.get('groups', [])) - desired_groups = set(params['groups']) + current_groups = set(extract_ids(current.get('groups') or [])) + desired_groups = set(extract_ids(params['groups'] or [])) if current_groups != desired_groups: return True @@ -299,8 +300,8 @@ def run_module(): if state == 'present': disabled_groups = module.params['disabled_management_groups'] if disabled_groups is not None: - current_disabled = set(current_settings.get('disabled_management_groups', [])) - desired_disabled = set(disabled_groups) + current_disabled = set(extract_ids(current_settings.get('disabled_management_groups') or [])) + desired_disabled = set(extract_ids(disabled_groups or [])) if current_disabled != desired_disabled: if not module.check_mode: diff --git a/plugins/modules/netbird_dns_zone.py b/plugins/modules/netbird_dns_zone.py index d0953a9..aaf11ac 100644 --- a/plugins/modules/netbird_dns_zone.py +++ b/plugins/modules/netbird_dns_zone.py @@ -168,6 +168,7 @@ 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 ) @@ -192,8 +193,8 @@ def zone_needs_update(current, params): if params.get('enable_search_domain') is not None and current.get('enable_search_domain') != params['enable_search_domain']: return True if params.get('distribution_groups') is not None: - current_groups = set(current.get('distribution_groups', []) or []) - desired_groups = set(params['distribution_groups'] or []) + current_groups = set(extract_ids(current.get('distribution_groups') or [])) + desired_groups = set(extract_ids(params['distribution_groups'] or [])) if current_groups != desired_groups: return True return False diff --git a/plugins/modules/netbird_group.py b/plugins/modules/netbird_group.py index 156745e..54d53ab 100644 --- a/plugins/modules/netbird_group.py +++ b/plugins/modules/netbird_group.py @@ -116,6 +116,7 @@ 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 ) @@ -136,14 +137,8 @@ def group_needs_update(current, desired): return True if 'peers' in desired and desired['peers'] is not None: - # API returns peers as dicts [{"id": "...", "name": "..."}] or null; - # desired peers are plain ID strings from the module parameter. - raw_peers = current.get('peers') or [] - if raw_peers and isinstance(raw_peers[0], dict): - current_peers = set(p['id'] for p in raw_peers) - else: - current_peers = set(raw_peers) - desired_peers = set(desired['peers']) + current_peers = set(extract_ids(current.get('peers') or [])) + desired_peers = set(extract_ids(desired['peers'] or [])) if current_peers != desired_peers: return True diff --git a/plugins/modules/netbird_network.py b/plugins/modules/netbird_network.py index ea6bd48..e755f1e 100644 --- a/plugins/modules/netbird_network.py +++ b/plugins/modules/netbird_network.py @@ -318,6 +318,7 @@ 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 ) @@ -343,7 +344,7 @@ def network_needs_update(current, params): def get_router_key(router): """Generate a unique key for a router based on peer/peer_groups.""" peer = router.get('peer', '') - peer_groups = tuple(sorted(router.get('peer_groups', []) or [])) + peer_groups = tuple(sorted(extract_ids(router.get('peer_groups') or []))) return (peer, peer_groups) @@ -366,8 +367,8 @@ def resource_needs_update(current, desired): return True if current.get('enabled', True) != desired.get('enabled', True): return True - current_groups = set(current.get('groups', []) or []) - desired_groups = set(desired.get('groups', []) or []) + current_groups = set(extract_ids(current.get('groups') or [])) + desired_groups = set(extract_ids(desired.get('groups') or [])) if current_groups != desired_groups: return True return False diff --git a/plugins/modules/netbird_policy.py b/plugins/modules/netbird_policy.py index 945f655..7d32f47 100644 --- a/plugins/modules/netbird_policy.py +++ b/plugins/modules/netbird_policy.py @@ -233,6 +233,7 @@ 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 ) @@ -283,6 +284,36 @@ def build_rules_data(rules): return [build_rule_data(rule) for rule in rules] +def normalize_rule(rule): + """Normalize a rule for comparison, extracting IDs from any dict references.""" + return { + 'name': rule.get('name', ''), + 'description': rule.get('description', ''), + 'enabled': rule.get('enabled', True), + 'sources': sorted(extract_ids(rule.get('sources') or [])), + 'destinations': sorted(extract_ids(rule.get('destinations') or [])), + 'bidirectional': rule.get('bidirectional', True), + 'protocol': rule.get('protocol', 'all'), + 'ports': sorted(rule.get('ports') or []), + 'action': rule.get('action', 'accept'), + } + + +def rules_need_update(current_rules, desired_rules): + """Check if rules need to be updated by comparing normalized representations.""" + current_rules = current_rules or [] + desired_rules = desired_rules or [] + if len(current_rules) != len(desired_rules): + return True + for current, desired in zip( + sorted(current_rules, key=lambda r: r.get('name', '')), + sorted(desired_rules, key=lambda r: r.get('name', '')) + ): + if normalize_rule(current) != normalize_rule(desired): + return True + return False + + def policy_needs_update(current, params): """Check if policy needs to be updated.""" if params.get('name') is not None and current.get('name') != params['name']: @@ -291,11 +322,14 @@ def policy_needs_update(current, params): return True if params.get('enabled') is not None and current.get('enabled') != params['enabled']: return True - if params.get('source_posture_checks') is not None and current.get('source_posture_checks') != params['source_posture_checks']: - return True - # For rules, always update if provided to ensure they match exactly + if params.get('source_posture_checks') is not None: + current_checks = set(extract_ids(current.get('source_posture_checks') or [])) + desired_checks = set(extract_ids(params['source_posture_checks'] or [])) + if current_checks != desired_checks: + return True if params.get('rules') is not None: - return True + if rules_need_update(current.get('rules'), params['rules']): + return True return False diff --git a/plugins/modules/netbird_route.py b/plugins/modules/netbird_route.py index e36c5ce..eaa5616 100644 --- a/plugins/modules/netbird_route.py +++ b/plugins/modules/netbird_route.py @@ -181,6 +181,7 @@ 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 ) @@ -207,15 +208,15 @@ def route_needs_update(current, params): # Check peer_groups if params.get('peer_groups') is not None: - current_groups = set(current.get('peer_groups', [])) - desired_groups = set(params['peer_groups']) + current_groups = set(extract_ids(current.get('peer_groups') or [])) + desired_groups = set(extract_ids(params['peer_groups'] or [])) if current_groups != desired_groups: return True - + # Check groups if params.get('groups') is not None: - current_groups = set(current.get('groups', [])) - desired_groups = set(params['groups']) + current_groups = set(extract_ids(current.get('groups') or [])) + desired_groups = set(extract_ids(params['groups'] or [])) if current_groups != desired_groups: return True diff --git a/plugins/modules/netbird_setup_key.py b/plugins/modules/netbird_setup_key.py index 909407b..747dae9 100644 --- a/plugins/modules/netbird_setup_key.py +++ b/plugins/modules/netbird_setup_key.py @@ -178,6 +178,7 @@ 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 ) @@ -198,8 +199,8 @@ def setup_key_needs_update(current, params): 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(current.get('auto_groups', [])) - desired_groups = set(params['auto_groups']) + 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 diff --git a/plugins/modules/netbird_user.py b/plugins/modules/netbird_user.py index 2000b6a..4f9a89b 100644 --- a/plugins/modules/netbird_user.py +++ b/plugins/modules/netbird_user.py @@ -158,6 +158,7 @@ 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 ) @@ -188,8 +189,8 @@ def user_needs_update(current, desired): return True if 'auto_groups' in desired and desired['auto_groups'] is not None: - current_groups = set(current.get('auto_groups', [])) - desired_groups = set(desired['auto_groups']) + current_groups = set(extract_ids(current.get('auto_groups') or [])) + desired_groups = set(extract_ids(desired['auto_groups'] or [])) if current_groups != desired_groups: return True