Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"fix: Fixed Server Group and Provisioning Key" #50

Merged
merged 7 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Zscaler Private Access (ZPA) Ansible Collection Changelog

## 1.4.1 (October, 28 2024)

### Notes

- Python Versions: **v3.9, v3.10, v3.11**

### Bug Fixes

- [PR #50](https://github.com/zscaler/zpacloud-ansible/pull/50) Fixed undetected drift issues within the resource `zpa_server_groups` related to the attribute `app_connector_group_ids`.
- [PR #50](https://github.com/zscaler/zpacloud-ansible/pull/50) Fixed undetected drift issues within the resource `zpa_application_segment_browser_access` related to the attribute `clientless_app_ids`.
- [PR #50](https://github.com/zscaler/zpacloud-ansible/pull/50) Fixed undetected drift issues within the resource `zpa_provisioning_key`.

## 1.4.0 (October, 9 2024)

### Notes
Expand Down
2 changes: 1 addition & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
html_title = "Ansible Collections Documentation"

# The full version, including alpha/beta/rc tags
release = "1.3.0"
release = "1.4.1"

# Disable the Copyright footer for Read the docs at the bottom of the page
# by setting property html_show_copyright = False
Expand Down
18 changes: 17 additions & 1 deletion docs/source/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,25 @@ Releases
Zscaler Private Access (ZPA) Ansible Collection Changelog
---------------------------------------------------------

Version 1.4.0
Version 1.4.1
==============

1.4.1 (October, 28 2024)
---------------------------

Notes
-----

- Python Versions: **v3.8, v3.9, v3.10, v3.11**

New Feature
------------

* (`#50 <https://github.com/zscaler/zpacloud-ansible/pull/50>`_) Fixed undetected drift issues within the resource `zpa_server_groups` related to the attribute `app_connector_group_ids`. (`Issue #49 <https://github.com/zscaler/zpacloud-ansible/pull/49>`_)
* (`#50 <https://github.com/zscaler/zpacloud-ansible/pull/50>`_) Fixed undetected drift issues within the resource `zpa_application_segment_browser_access` related to the attribute `clientless_app_ids`.
* (`#50 <https://github.com/zscaler/zpacloud-ansible/pull/50>`_) Fixed undetected drift issues within the resource `zpa_provisioning_key`.


1.4.0 (October, 9 2024)
---------------------------

Expand Down
14 changes: 12 additions & 2 deletions plugins/module_utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,22 @@ def normalize_app(app):
"check_control_deployment_status",
"controls_facts",
"lss_app_connector_group",
"app_connector_group_ids",
"clientless_app_ids",
]
for attr in computed_values:
normalized.pop(attr, None)

# Fix: Convert app_connector_groups to app_connector_group_ids
if "app_connector_groups" in app:
normalized["app_connector_group_ids"] = [
group["id"] for group in app["app_connector_groups"]
]

# Fix: Convert clientless_apps to clientless_app_ids
# if "clientless_apps" in app:
# normalized["clientless_app_ids"] = [
# segment["id"] for segment in app["clientless_apps"]
# ]

if "tcp_keep_alive" in normalized:
normalized["tcp_keep_alive"] = convert_str_to_bool(normalized["tcp_keep_alive"])

Expand Down
4 changes: 1 addition & 3 deletions plugins/modules/zpa_app_connector_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,7 @@ def main():
argument_spec = ZPAClientHelper.zpa_argument_spec()
argument_spec.update(
id=dict(type="str", required=False),
ids=dict(
type="list", elements="str", required=False
),
ids=dict(type="list", elements="str", required=False),
name=dict(type="str", required=False),
description=dict(type="str", required=False),
enabled=dict(type="bool", required=False),
Expand Down
159 changes: 118 additions & 41 deletions plugins/modules/zpa_provisioning_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,31 @@
)


def normalize_provisioning_key(group):
def normalize_provisioning_key(prov_key):
"""
Normalize provisioning key data by setting computed values.
"""
normalized = group.copy()
computed_values = ["creation_time", "modified_by", "modified_time"]
normalized = prov_key.copy()
computed_values = [
"id",
"creation_time",
"modified_by",
"modified_time",
"enrollment_cert_name",
"provisioning_key",
"zcomponent_name",
"usage_count",
]
for attr in computed_values:
normalized.pop(attr, None)

# Map 'zcomponent_id' to 'component_id' for consistency
if "zcomponent_id" in normalized:
normalized["component_id"] = normalized.pop("zcomponent_id")

# Remove `key_type` since it's only for URL construction and not part of the API data
normalized.pop("key_type", None)

return normalized


Expand Down Expand Up @@ -148,15 +165,17 @@ def core(module):
"enabled",
"max_usage",
"enrollment_cert_id",
"usage_count",
"component_id",
"component_id", # The user-facing attribute
"key_type",
]
}
provisioning_key["enrollment_cert_id"] = enrollment_cert_id # Set the fetched ID
provisioning_key_id = module.params.get("id", None)
existing_key = None

# Debugging: Display the desired state
# module.warn(f"Desired provisioning key: {provisioning_key}")

existing_key = None
if provisioning_key_id is not None:
existing_key = client.provisioning.get_provisioning_key(
key_id=provisioning_key_id, key_type=key_type
Expand All @@ -168,15 +187,32 @@ def core(module):
existing_key = k
break

normalized_key = normalize_provisioning_key(provisioning_key)
normalized_existing_key = (
normalize_provisioning_key(existing_key) if existing_key else {}
)
differences_detected = any(
normalized_key.get(key) != normalized_existing_key.get(key)
for key in normalized_key
if key not in ["id"]
)
# Debugging: Display the current state (what Ansible sees from the API)
# module.warn(f"Current provisioning key from API: {existing_key}")

# Set defaults for desired state to avoid drift due to None values
if provisioning_key.get("enabled") is None:
provisioning_key["enabled"] = True # Set to True if not explicitly set

desired_key = normalize_provisioning_key(provisioning_key)
current_key = normalize_provisioning_key(existing_key) if existing_key else {}

# Handle the component_id/zcomponent_id mapping during comparison only
if "zcomponent_id" in current_key:
current_key["component_id"] = current_key.pop("zcomponent_id")

# Debugging: Show normalized values for comparison
# module.warn(f"Normalized Desired: {desired_key}")
# module.warn(f"Normalized Current: {current_key}")

fields_to_exclude = ["id", "key_type"]
differences_detected = False
for key, value in desired_key.items():
# Debugging: Track comparisons for each key-value pair
# module.warn(f"Comparing key: {key}, Desired: {value}, Current: {current_key.get(key)}")
if key not in fields_to_exclude and current_key.get(key) != value:
differences_detected = True
# module.warn(f"Difference detected in {key}. Current: {current_key.get(key)}, Desired: {value}")

if module.check_mode:
# If in check mode, report changes and exit
Expand All @@ -187,34 +223,75 @@ def core(module):
else:
module.exit_json(changed=False)

if existing_key is not None and differences_detected:
# Ensure 'key_type' is not passed twice
update_params = deleteNone(provisioning_key)
update_params.pop("key_type", None) # Remove key_type to prevent duplication

# Corrected update call
existing_key = client.provisioning.update_provisioning_key(
key_id=existing_key.get("id"), # Passing key_id directly
key_type=key_type, # Passing key_type directly
**update_params,
)
module.exit_json(changed=True, data=existing_key)

elif not existing_key:
new_key = client.provisioning.add_provisioning_key(
**deleteNone(provisioning_key)
)
module.exit_json(changed=True, data=new_key)
else:
module.exit_json(changed=False, data=existing_key)

if state == "absent":
client.provisioning.delete_provisioning_key(
# module.warn(f"Final payload being sent to SDK: {provisioning_key}")
if existing_key is not None:
id = existing_key.get("id")
existing_key.update(provisioning_key)
existing_key["id"] = id

# module.warn(f"Final payload being sent to SDK: {server_group}")
if state == "present":
if existing_key is not None:
if differences_detected:
"""Update"""
existing_key = deleteNone(
dict(
key_id=existing_key.get("id"),
name=existing_key.get("name", None),
enabled=existing_key.get("enabled", None),
max_usage=existing_key.get("max_usage", None),
enrollment_cert_id=existing_key.get("enrollment_cert_id", None),
component_id=existing_key.get("component_id", None),
key_type=existing_key.get("key_type", None),
)
)
# module.warn(f"Payload Update for SDK: {existing_key}")
existing_key = client.provisioning.update_provisioning_key(
**existing_key
)
module.exit_json(changed=True, data=existing_key)
else:
"""No Changes Needed"""
module.exit_json(changed=False, data=existing_key)
else:
"""Create"""
provisioning_key = deleteNone(
dict(
name=provisioning_key.get("name", None),
enabled=provisioning_key.get("enabled", None),
max_usage=provisioning_key.get("max_usage", None),
enrollment_cert_id=provisioning_key.get("enrollment_cert_id", None),
component_id=provisioning_key.get("component_id", None),
key_type=provisioning_key.get("key_type", None),
)
)
provisioning_key = client.provisioning.add_provisioning_key(
**provisioning_key
).to_dict()
module.exit_json(changed=True, data=provisioning_key)

elif state == "absent" and existing_key is not None:
# Debugging: Log the provisioning key ID and key type being passed for deletion
# module.warn(f"Attempting to delete provisioning key with ID: {provisioning_key_id} and key_type: {key_type}")
if not provisioning_key_id:
# module.warn(f"Provisioning key ID is missing, fetching it from existing_key: {existing_key}")
provisioning_key_id = existing_key.get("id")
# Debugging: Log the final provisioning key ID and key_type before making the delete request
# module.warn(f"Final provisioning key ID for deletion: {provisioning_key_id}, key_type: {key_type}")
code = client.provisioning.delete_provisioning_key(
key_id=provisioning_key_id, key_type=key_type
)
module.exit_json(changed=True)

module.exit_json(changed=False, data={})
# Debugging: Log the API response code for deletion
# module.warn(f"Deletion API response code: {code}")
if code > 299:
# module.warn(f"Deletion failed with status code: {code}")
module.exit_json(changed=False, data=None)
# module.warn(f"Provisioning key with ID {provisioning_key_id} was successfully deleted.")
module.exit_json(changed=True, data=existing_key)
# Debugging: Log if no existing key is found when state is absent
elif state == "absent" and existing_key is None:
# module.warn(f"No existing provisioning key found for deletion with name: {module.params.get('name')}")
module.exit_json(changed=False, data=None)


def main():
Expand Down
60 changes: 37 additions & 23 deletions plugins/modules/zpa_server_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ def core(module):
]
for param_name in params:
server_group[param_name] = module.params.get(param_name, None)

# Debugging: Display the desired state
# module.warn(f"Desired server group: {server_group}")

group_id = server_group.get("id", None)
group_name = server_group.get("name", None)

Expand All @@ -157,17 +161,25 @@ def core(module):
existing_server_group = group_
break

# Debugging: Display the current state (what Ansible sees from the API)
# module.warn(f"Current server group from API: {existing_server_group}")

desired_app = normalize_app(server_group)
current_app = normalize_app(existing_server_group) if existing_server_group else {}

# Debugging: Show normalized values for comparison
# module.warn(f"Normalized Desired: {desired_app}")
# module.warn(f"Normalized Current: {current_app}")

fields_to_exclude = ["id"]
differences_detected = False
for key, value in desired_app.items():
# Debugging: Track comparisons for each key-value pair
# module.warn(f"Comparing key: {key}, Desired: {value}, Current: {current_app.get(key)}")

if key not in fields_to_exclude and current_app.get(key) != value:
differences_detected = True
# module.warn(
# f"Difference detected in {key}. Current: {current_app.get(key)}, Desired: {value}"
# )
# module.warn(f"Difference detected in {key}. Current: {current_app.get(key)}, Desired: {value}")

if module.check_mode:
# If in check mode, report changes and exit
Expand All @@ -185,25 +197,27 @@ def core(module):
existing_server_group.update(server_group)
existing_server_group["id"] = id

# module.warn(f"Final payload being sent to SDK: {server_group}")
if state == "present":
if existing_server_group is not None:
if differences_detected:
"""Update"""
existing_server_group = deleteNone(
{
"group_id": existing_server_group.get("id"),
"name": existing_server_group.get("name"),
"description": existing_server_group.get("description"),
"enabled": existing_server_group.get("enabled"),
"app_connector_group_ids": existing_server_group.get(
"app_connector_group_ids"
dict(
group_id=existing_server_group.get("id"),
name=existing_server_group.get("name", None),
description=existing_server_group.get("description", None),
enabled=existing_server_group.get("enabled", None),
app_connector_group_ids=existing_server_group.get(
"app_connector_group_ids", None
),
"dynamic_discovery": existing_server_group.get(
"dynamic_discovery"
dynamic_discovery=existing_server_group.get(
"dynamic_discovery", None
),
"server_ids": existing_server_group.get("server_ids"),
}
server_ids=existing_server_group.get("server_ids", None),
)
)
# module.warn(f"Payload Update for SDK: {existing_server_group}")
existing_server_group = client.server_groups.update_group(
**existing_server_group
)
Expand All @@ -214,16 +228,16 @@ def core(module):
else:
"""Create"""
server_group = deleteNone(
{
"name": server_group.get("name"),
"app_connector_group_ids": server_group.get(
"app_connector_group_ids"
dict(
name=server_group.get("name", None),
app_connector_group_ids=server_group.get(
"app_connector_group_ids", None
),
"description": server_group.get("description"),
"enabled": server_group.get("enabled"),
"dynamic_discovery": server_group.get("dynamic_discovery"),
"server_ids": server_group.get("server_ids"),
}
description=server_group.get("description", None),
enabled=server_group.get("enabled", None),
dynamic_discovery=server_group.get("dynamic_discovery", None),
server_ids=server_group.get("server_ids", None),
)
)
server_group = client.server_groups.add_group(**server_group).to_dict()
module.exit_json(changed=True, data=server_group)
Expand Down
Loading