From 8b8a3a72ececfafed693ba3eaaecf295b60f9509 Mon Sep 17 00:00:00 2001 From: William Guilherme Date: Tue, 19 Sep 2023 21:19:07 -0700 Subject: [PATCH] feat: Initial Commit --- .ansible-lint | 127 +++++ .github/ISSUE_TEMPLATE/bug_report.md | 53 ++ .../ISSUE_TEMPLATE/documentation_problem.md | 21 + .github/ISSUE_TEMPLATE/feature_request.md | 29 ++ .github/dependabot.yml | 6 + .github/do-release.sh | 5 + .github/publishing.md | 18 + .github/set-version.sh | 43 ++ .github/workflows/CI.yml | 229 +++++++++ .../automationhub_token_keepalive.yml | 19 + .gitignore | 114 ++++ .isort.cfg | 7 + .nojekyll | 0 .releaserc.json | 45 ++ CHANGELOG.md | 0 CODE_OF_CONDUCT.md | 76 +++ LICENSE | 21 + Makefile | 69 +++ README.md | 160 ++++++ SUPPORT.md | 10 + docs/contributing.md | 144 ++++++ docs/history.md | 10 + docs/source/authors.rst | 14 + docs/source/conf.py | 116 +++++ docs/source/examples.rst | 254 +++++++++ docs/source/history.rst | 5 + docs/source/index.rst | 53 ++ docs/source/license.rst | 5 + docs/source/modules.rst | 8 + docs/templates/module.rst.j2 | 186 +++++++ examples/zpa_app_connector_groups.yml | 69 +++ galaxy.yml | 62 +++ meta/runtime.yml | 2 + plugins/doc_fragments/fragments.py | 35 ++ plugins/module_utils/zpa_client.py | 83 +++ .../zpa_app_connector_controller_info.py | 114 ++++ plugins/modules/zpa_app_connector_groups.py | 402 +++++++++++++++ .../modules/zpa_app_connector_groups_info.py | 115 +++++ plugins/modules/zpa_application_segment.py | 428 ++++++++++++++++ .../zpa_application_segment_browser_access.py | 485 ++++++++++++++++++ ...application_segment_browser_access_info.py | 123 +++++ .../modules/zpa_application_segment_info.py | 113 ++++ plugins/modules/zpa_application_server.py | 198 +++++++ .../modules/zpa_application_server_info.py | 116 +++++ plugins/modules/zpa_ba_certificate_info.py | 120 +++++ .../modules/zpa_cloud_connector_group_info.py | 115 +++++ .../zpa_enrollement_certificate_info.py | 132 +++++ plugins/modules/zpa_idp_controller_info.py | 114 ++++ plugins/modules/zpa_lss_client_types_info.py | 117 +++++ plugins/modules/zpa_lss_config_controller.py | 475 +++++++++++++++++ .../modules/zpa_lss_config_controller_info.py | 115 +++++ .../zpa_lss_config_log_types_formats_info.py | 111 ++++ .../zpa_lss_config_status_codes_info.py | 194 +++++++ plugins/modules/zpa_machine_group_info.py | 114 ++++ plugins/modules/zpa_policy_access_rule.py | 420 +++++++++++++++ .../modules/zpa_policy_access_rule_info.py | 117 +++++ plugins/modules/zpa_policy_forwarding_rule.py | 389 ++++++++++++++ .../zpa_policy_forwarding_rule_info.py | 119 +++++ plugins/modules/zpa_policy_timeout_rule.py | 377 ++++++++++++++ .../modules/zpa_policy_timeout_rule_info.py | 115 +++++ plugins/modules/zpa_posture_profile_info.py | 123 +++++ plugins/modules/zpa_provisioning_key.py | 220 ++++++++ plugins/modules/zpa_provisioning_key_info.py | 139 +++++ plugins/modules/zpa_saml_attribute_info.py | 135 +++++ .../modules/zpa_scim_attribute_header_info.py | 139 +++++ plugins/modules/zpa_scim_group_info.py | 126 +++++ plugins/modules/zpa_segment_group.py | 203 ++++++++ plugins/modules/zpa_segment_group_info.py | 114 ++++ plugins/modules/zpa_server_group.py | 243 +++++++++ plugins/modules/zpa_server_group_info.py | 114 ++++ plugins/modules/zpa_service_edge_groups.py | 344 +++++++++++++ .../modules/zpa_service_edge_groups_info.py | 116 +++++ plugins/modules/zpa_trusted_networks_info.py | 122 +++++ pyproject.toml | 43 ++ requirements.txt | 131 +++++ tests/integration/ansible.cfg | 2 + tests/integration/group_vars/all.yml | 2 + .../integration_config.yml.template | 4 + .../zpa_app_connector_controller_info/aliases | 0 .../defaults/main.yml | 0 ...test_zpa_app_connector_controller_info.yml | 19 + .../defaults/main.yml | 13 + .../zpa_app_connector_groups/tasks/main.yml | 151 ++++++ .../zpa_app_connector_groups_info/aliases | 0 .../tasks/main.yml | 23 + .../zpa_application_segment/defaults/main.yml | 12 + .../zpa_application_segment/tasks/main.yml | 287 +++++++++++ .../defaults/main.yml | 0 .../tasks/main.yml | 12 + .../zpa_application_segment_info/aliases | 0 .../defaults/main.yml | 0 .../tasks/main.yml | 25 + .../zpa_application_server/defaults/main.yml | 5 + .../zpa_application_server/tasks/main.yml | 61 +++ .../targets/zpa_ba_certificate_info/aliases | 0 .../zpa_ba_certificate_info/defaults/main.yml | 0 .../zpa_ba_certificate_info/tasks/main.yml | 25 + .../zpa_cloud_connector_group_info/aliases | 0 .../defaults/main.yml | 0 .../tasks/main.yml | 25 + .../zpa_customer_version_profile_info/aliases | 0 .../defaults/main.yml | 0 .../tasks/main.yml | 25 + .../zpa_enrollement_certificate_info/aliases | 0 .../defaults/main.yml | 0 .../tasks/main.yml | 92 ++++ .../targets/zpa_idp_controller_info/aliases | 0 .../zpa_idp_controller_info/defaults/main.yml | 0 .../zpa_idp_controller_info/tasks/main.yml | 25 + .../targets/zpa_lss_client_types_info/aliases | 0 .../defaults/main.yml | 0 .../zpa_lss_client_types_info/tasks/main.yml | 25 + .../aliases | 0 .../defaults/main.yml | 0 .../tasks/main.yml | 117 +++++ .../zpa_lss_config_status_codes_info/aliases | 0 .../defaults/main.yml | 0 .../tasks/main.yml | 25 + .../targets/zpa_machine_group_info/aliases | 0 .../zpa_machine_group_info/defaults/main.yml | 0 .../zpa_machine_group_info/tasks/main.yml | 25 + .../targets/zpa_policy_access_rule/aliases | 0 .../zpa_policy_access_rule/defaults/main.yml | 0 .../zpa_policy_access_rule/tasks/main.yml | 123 +++++ .../zpa_policy_access_rule_info/aliases | 0 .../defaults/main.yml | 0 .../tasks/main.yml | 25 + .../targets/zpa_posture_profile_info/aliases | 0 .../defaults/main.yml | 0 .../zpa_posture_profile_info/tasks/main.yml | 25 + .../targets/zpa_provisioning_key/aliases | 0 .../zpa_provisioning_key/defaults/main.yml | 0 .../zpa_provisioning_key/tasks/main.yml | 99 ++++ .../targets/zpa_saml_attribute_info/aliases | 0 .../zpa_saml_attribute_info/defaults/main.yml | 2 + .../zpa_saml_attribute_info/tasks/main.yml | 33 ++ .../zpa_scim_attribute_header_info/aliases | 0 .../defaults/main.yml | 1 + .../tasks/main.yml | 27 + .../targets/zpa_scim_group_info/aliases | 0 .../zpa_scim_group_info/defaults/main.yml | 1 + .../zpa_scim_group_info/tasks/main.yml | 27 + .../zpa_segment_group/defaults/main.yml | 6 + .../targets/zpa_segment_group/tasks/main.yml | 129 +++++ .../zpa_segment_group_info/defaults/main.yml | 0 .../zpa_segment_group_info/tasks/main.yml | 25 + .../zpa_server_group/defaults/main.yml | 4 + .../targets/zpa_server_group/tasks/main.yml | 181 +++++++ .../targets/zpa_server_group_info/aliases | 0 .../zpa_server_group_info/defaults/main.yml | 0 .../zpa_server_group_info/tasks/main.yml | 25 + .../zpa_service_edge_groups/defaults/main.yml | 12 + .../zpa_service_edge_groups/tasks/main.yml | 148 ++++++ .../zpa_service_edge_groups_info/aliases | 0 .../defaults/main.yml | 0 .../tasks/main.yml | 25 + .../targets/zpa_trusted_networks_info/aliases | 0 .../defaults/main.yml | 0 .../zpa_trusted_networks_info/tasks/main.yml | 25 + .../test_zpa_app_connector_group.py | 107 ++++ .../test_zpa_application_segment.py | 123 +++++ .../test_zpa_policy_access_rule.py | 164 ++++++ .../module_utils/test_zpa_segment_group.py | 97 ++++ .../module_utils/test_zpa_server_group.py | 163 ++++++ tests/utils/render.py | 29 ++ 165 files changed, 12274 insertions(+) create mode 100644 .ansible-lint create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/documentation_problem.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/dependabot.yml create mode 100644 .github/do-release.sh create mode 100644 .github/publishing.md create mode 100644 .github/set-version.sh create mode 100644 .github/workflows/CI.yml create mode 100644 .github/workflows/automationhub_token_keepalive.yml create mode 100644 .gitignore create mode 100644 .isort.cfg create mode 100644 .nojekyll create mode 100644 .releaserc.json create mode 100644 CHANGELOG.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 SUPPORT.md create mode 100644 docs/contributing.md create mode 100644 docs/history.md create mode 100644 docs/source/authors.rst create mode 100644 docs/source/conf.py create mode 100644 docs/source/examples.rst create mode 100644 docs/source/history.rst create mode 100644 docs/source/index.rst create mode 100644 docs/source/license.rst create mode 100644 docs/source/modules.rst create mode 100644 docs/templates/module.rst.j2 create mode 100644 examples/zpa_app_connector_groups.yml create mode 100644 galaxy.yml create mode 100644 meta/runtime.yml create mode 100644 plugins/doc_fragments/fragments.py create mode 100644 plugins/module_utils/zpa_client.py create mode 100644 plugins/modules/zpa_app_connector_controller_info.py create mode 100644 plugins/modules/zpa_app_connector_groups.py create mode 100644 plugins/modules/zpa_app_connector_groups_info.py create mode 100644 plugins/modules/zpa_application_segment.py create mode 100644 plugins/modules/zpa_application_segment_browser_access.py create mode 100644 plugins/modules/zpa_application_segment_browser_access_info.py create mode 100644 plugins/modules/zpa_application_segment_info.py create mode 100644 plugins/modules/zpa_application_server.py create mode 100644 plugins/modules/zpa_application_server_info.py create mode 100644 plugins/modules/zpa_ba_certificate_info.py create mode 100644 plugins/modules/zpa_cloud_connector_group_info.py create mode 100644 plugins/modules/zpa_enrollement_certificate_info.py create mode 100644 plugins/modules/zpa_idp_controller_info.py create mode 100644 plugins/modules/zpa_lss_client_types_info.py create mode 100644 plugins/modules/zpa_lss_config_controller.py create mode 100644 plugins/modules/zpa_lss_config_controller_info.py create mode 100644 plugins/modules/zpa_lss_config_log_types_formats_info.py create mode 100644 plugins/modules/zpa_lss_config_status_codes_info.py create mode 100644 plugins/modules/zpa_machine_group_info.py create mode 100644 plugins/modules/zpa_policy_access_rule.py create mode 100644 plugins/modules/zpa_policy_access_rule_info.py create mode 100644 plugins/modules/zpa_policy_forwarding_rule.py create mode 100644 plugins/modules/zpa_policy_forwarding_rule_info.py create mode 100644 plugins/modules/zpa_policy_timeout_rule.py create mode 100644 plugins/modules/zpa_policy_timeout_rule_info.py create mode 100644 plugins/modules/zpa_posture_profile_info.py create mode 100644 plugins/modules/zpa_provisioning_key.py create mode 100644 plugins/modules/zpa_provisioning_key_info.py create mode 100644 plugins/modules/zpa_saml_attribute_info.py create mode 100644 plugins/modules/zpa_scim_attribute_header_info.py create mode 100644 plugins/modules/zpa_scim_group_info.py create mode 100644 plugins/modules/zpa_segment_group.py create mode 100644 plugins/modules/zpa_segment_group_info.py create mode 100644 plugins/modules/zpa_server_group.py create mode 100644 plugins/modules/zpa_server_group_info.py create mode 100644 plugins/modules/zpa_service_edge_groups.py create mode 100644 plugins/modules/zpa_service_edge_groups_info.py create mode 100644 plugins/modules/zpa_trusted_networks_info.py create mode 100644 pyproject.toml create mode 100644 requirements.txt create mode 100644 tests/integration/ansible.cfg create mode 100644 tests/integration/group_vars/all.yml create mode 100644 tests/integration/integration_config.yml.template create mode 100644 tests/integration/targets/zpa_app_connector_controller_info/aliases create mode 100644 tests/integration/targets/zpa_app_connector_controller_info/defaults/main.yml create mode 100644 tests/integration/targets/zpa_app_connector_controller_info/tasks/test_zpa_app_connector_controller_info.yml create mode 100644 tests/integration/targets/zpa_app_connector_groups/defaults/main.yml create mode 100644 tests/integration/targets/zpa_app_connector_groups/tasks/main.yml create mode 100644 tests/integration/targets/zpa_app_connector_groups_info/aliases create mode 100644 tests/integration/targets/zpa_app_connector_groups_info/tasks/main.yml create mode 100644 tests/integration/targets/zpa_application_segment/defaults/main.yml create mode 100644 tests/integration/targets/zpa_application_segment/tasks/main.yml create mode 100644 tests/integration/targets/zpa_application_segment_browser_access/defaults/main.yml create mode 100644 tests/integration/targets/zpa_application_segment_browser_access/tasks/main.yml create mode 100644 tests/integration/targets/zpa_application_segment_info/aliases create mode 100644 tests/integration/targets/zpa_application_segment_info/defaults/main.yml create mode 100644 tests/integration/targets/zpa_application_segment_info/tasks/main.yml create mode 100644 tests/integration/targets/zpa_application_server/defaults/main.yml create mode 100644 tests/integration/targets/zpa_application_server/tasks/main.yml create mode 100644 tests/integration/targets/zpa_ba_certificate_info/aliases create mode 100644 tests/integration/targets/zpa_ba_certificate_info/defaults/main.yml create mode 100644 tests/integration/targets/zpa_ba_certificate_info/tasks/main.yml create mode 100644 tests/integration/targets/zpa_cloud_connector_group_info/aliases create mode 100644 tests/integration/targets/zpa_cloud_connector_group_info/defaults/main.yml create mode 100644 tests/integration/targets/zpa_cloud_connector_group_info/tasks/main.yml create mode 100644 tests/integration/targets/zpa_customer_version_profile_info/aliases create mode 100644 tests/integration/targets/zpa_customer_version_profile_info/defaults/main.yml create mode 100644 tests/integration/targets/zpa_customer_version_profile_info/tasks/main.yml create mode 100644 tests/integration/targets/zpa_enrollement_certificate_info/aliases create mode 100644 tests/integration/targets/zpa_enrollement_certificate_info/defaults/main.yml create mode 100644 tests/integration/targets/zpa_enrollement_certificate_info/tasks/main.yml create mode 100644 tests/integration/targets/zpa_idp_controller_info/aliases create mode 100644 tests/integration/targets/zpa_idp_controller_info/defaults/main.yml create mode 100644 tests/integration/targets/zpa_idp_controller_info/tasks/main.yml create mode 100644 tests/integration/targets/zpa_lss_client_types_info/aliases create mode 100644 tests/integration/targets/zpa_lss_client_types_info/defaults/main.yml create mode 100644 tests/integration/targets/zpa_lss_client_types_info/tasks/main.yml create mode 100644 tests/integration/targets/zpa_lss_config_log_types_formats_info/aliases create mode 100644 tests/integration/targets/zpa_lss_config_log_types_formats_info/defaults/main.yml create mode 100644 tests/integration/targets/zpa_lss_config_log_types_formats_info/tasks/main.yml create mode 100644 tests/integration/targets/zpa_lss_config_status_codes_info/aliases create mode 100644 tests/integration/targets/zpa_lss_config_status_codes_info/defaults/main.yml create mode 100644 tests/integration/targets/zpa_lss_config_status_codes_info/tasks/main.yml create mode 100644 tests/integration/targets/zpa_machine_group_info/aliases create mode 100644 tests/integration/targets/zpa_machine_group_info/defaults/main.yml create mode 100644 tests/integration/targets/zpa_machine_group_info/tasks/main.yml create mode 100644 tests/integration/targets/zpa_policy_access_rule/aliases create mode 100644 tests/integration/targets/zpa_policy_access_rule/defaults/main.yml create mode 100644 tests/integration/targets/zpa_policy_access_rule/tasks/main.yml create mode 100644 tests/integration/targets/zpa_policy_access_rule_info/aliases create mode 100644 tests/integration/targets/zpa_policy_access_rule_info/defaults/main.yml create mode 100644 tests/integration/targets/zpa_policy_access_rule_info/tasks/main.yml create mode 100644 tests/integration/targets/zpa_posture_profile_info/aliases create mode 100644 tests/integration/targets/zpa_posture_profile_info/defaults/main.yml create mode 100644 tests/integration/targets/zpa_posture_profile_info/tasks/main.yml create mode 100644 tests/integration/targets/zpa_provisioning_key/aliases create mode 100644 tests/integration/targets/zpa_provisioning_key/defaults/main.yml create mode 100644 tests/integration/targets/zpa_provisioning_key/tasks/main.yml create mode 100644 tests/integration/targets/zpa_saml_attribute_info/aliases create mode 100644 tests/integration/targets/zpa_saml_attribute_info/defaults/main.yml create mode 100644 tests/integration/targets/zpa_saml_attribute_info/tasks/main.yml create mode 100644 tests/integration/targets/zpa_scim_attribute_header_info/aliases create mode 100644 tests/integration/targets/zpa_scim_attribute_header_info/defaults/main.yml create mode 100644 tests/integration/targets/zpa_scim_attribute_header_info/tasks/main.yml create mode 100644 tests/integration/targets/zpa_scim_group_info/aliases create mode 100644 tests/integration/targets/zpa_scim_group_info/defaults/main.yml create mode 100644 tests/integration/targets/zpa_scim_group_info/tasks/main.yml create mode 100644 tests/integration/targets/zpa_segment_group/defaults/main.yml create mode 100644 tests/integration/targets/zpa_segment_group/tasks/main.yml create mode 100644 tests/integration/targets/zpa_segment_group_info/defaults/main.yml create mode 100644 tests/integration/targets/zpa_segment_group_info/tasks/main.yml create mode 100644 tests/integration/targets/zpa_server_group/defaults/main.yml create mode 100644 tests/integration/targets/zpa_server_group/tasks/main.yml create mode 100644 tests/integration/targets/zpa_server_group_info/aliases create mode 100644 tests/integration/targets/zpa_server_group_info/defaults/main.yml create mode 100644 tests/integration/targets/zpa_server_group_info/tasks/main.yml create mode 100644 tests/integration/targets/zpa_service_edge_groups/defaults/main.yml create mode 100644 tests/integration/targets/zpa_service_edge_groups/tasks/main.yml create mode 100644 tests/integration/targets/zpa_service_edge_groups_info/aliases create mode 100644 tests/integration/targets/zpa_service_edge_groups_info/defaults/main.yml create mode 100644 tests/integration/targets/zpa_service_edge_groups_info/tasks/main.yml create mode 100644 tests/integration/targets/zpa_trusted_networks_info/aliases create mode 100644 tests/integration/targets/zpa_trusted_networks_info/defaults/main.yml create mode 100644 tests/integration/targets/zpa_trusted_networks_info/tasks/main.yml create mode 100644 tests/unit/plugins/module_utils/test_zpa_app_connector_group.py create mode 100644 tests/unit/plugins/module_utils/test_zpa_application_segment.py create mode 100644 tests/unit/plugins/module_utils/test_zpa_policy_access_rule.py create mode 100644 tests/unit/plugins/module_utils/test_zpa_segment_group.py create mode 100644 tests/unit/plugins/module_utils/test_zpa_server_group.py create mode 100755 tests/utils/render.py diff --git a/.ansible-lint b/.ansible-lint new file mode 100644 index 0000000..e1b17cf --- /dev/null +++ b/.ansible-lint @@ -0,0 +1,127 @@ +--- +# .ansible-lint + +profile: production + +# Allows dumping of results in SARIF format +# sarif_file: result.sarif + +# exclude_paths included in this file are parsed relative to this file's location +# and not relative to the CWD of execution. CLI arguments passed to the --exclude +# option are parsed relative to the CWD of execution. +exclude_paths: +# - .cache/ # implicit unless exclude_paths is defined in config + - .github/ +# - test/fixtures/formatting-before/ +# - test/fixtures/formatting-prettier/ +# parseable: true +# quiet: true +# strict: true +# verbosity: 1 + +# Mock modules or roles in order to pass ansible-playbook --syntax-check +# mock_modules: +# - zscaler.zpacloud.zpacloud_application_segment +# - zuul_return +# # note the foo.bar is invalid as being neither a module or a collection +# - fake_namespace.fake_collection.fake_module +# - fake_namespace.fake_collection.fake_module.fake_submodule +# mock_roles: +# - mocked_role +# - author.role_name # old standalone galaxy role +# - fake_namespace.fake_collection.fake_role # role within a collection + +# Enable checking of loop variable prefixes in roles +# loop_var_prefix: "^(__|{role}_)" + +# Enforce variable names to follow pattern below, in addition to Ansible own +# requirements, like avoiding python identifiers. To disable add `var-naming` +# to skip_list. +# var_naming_pattern: "^[a-z_][a-z0-9_]*$" + +# use_default_rules: true +# Load custom rules from this specific folder +# rulesdir: +# - ./rule/directory/ + +# Ansible-lint is able to recognize and load skip rules stored inside +# `.ansible-lint-ignore` (or `.config/ansible-lint-ignore.txt`) files. +# To skip a rule just enter filename and tag, like "playbook.yml package-latest" +# on a new line. +# Optionally you can add comments after the tag, prefixed by "#". We discourage +# the use of skip_list below because that will hide violations from the output. +# When putting ignores inside the ignore file, they are marked as ignored, but +# still visible, making it easier to address later. +# skip_list: +# - role-name + +# Ansible-lint does not automatically load rules that have the 'opt-in' tag. +# You must enable opt-in rules by listing each rule 'id' below. +enable_list: + # - args + # - empty-string-compare # opt-in + # - no-log-password # opt-in + # - no-same-owner # opt-in + # - name[prefix] # opt-in + # add yaml here if you want to avoid ignoring yaml checks when yamllint + # library is missing. Normally its absence just skips using that rule. + - yaml +# Report only a subset of tags and fully ignore any others +# tags: +# - jinja[spacing] + +# Ansible-lint does not fail on warnings from the rules or tags listed below +# warn_list: + # - skip_this_tag + # - experimental # experimental is included in the implicit list + # - role-name + # - yaml[document-start] # you can also use sub-rule matches + +# Some rules can transform files to fix (or make it easier to fix) identified +# errors. `ansible-lint --write` will reformat YAML files and run these transforms. +# By default it will run all transforms (effectively `write_list: ["all"]`). +# You can disable running transforms by setting `write_list: ["none"]`. +# Or only enable a subset of rule transforms by listing rules/tags here. +# write_list: +# - all + +# Offline mode disables installation of requirements.yml and schema refreshing +# offline: true + +# Return success if number of violations compared with previous git +# commit has not increased. This feature works only in git +# repositories. +# progressive: false + +# Define required Ansible's variables to satisfy syntax check +# extra_vars: +# foo: bar +# multiline_string_variable: | +# line1 +# line2 +# complex_variable: ":{;\t$()" + +# Uncomment to enforce action validation with tasks, usually is not +# needed as Ansible syntax check also covers it. +# skip_action_validation: false + +# List of additional kind:pattern to be added at the top of the default +# match list, first match determines the file kind. +# kinds: + # - playbook: "**/examples/*.{yml,yaml}" + # - galaxy: "**/folder/galaxy.yml" + # - tasks: "**/tasks/*.yml" + # - vars: "**/vars/*.yml" + # - meta: "**/meta/main.yml" + # - yaml: "**/*.yaml-too" + +# List of additional collections to allow in only-builtins rule. +# only_builtins_allow_collections: +# - example_ns.example_collection + +# List of additions modules to allow in only-builtins rule. +# only_builtins_allow_modules: +# - example_module + +# Allow setting custom prefix for name[prefix] rule +# task_name_prefix: "{stem} | " diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..0301062 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,53 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +## Describe the bug + + + + +## Expected behavior + + + +## Current behavior + + + +## Possible solution + + + + +## Steps to reproduce + + + + +1. +2. +3. +4. + +## Screenshots + + + +## Context + + + + +## Your Environment + + + +- Collection: +- Python: +- Ansible: diff --git a/.github/ISSUE_TEMPLATE/documentation_problem.md b/.github/ISSUE_TEMPLATE/documentation_problem.md new file mode 100644 index 0000000..28f364c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation_problem.md @@ -0,0 +1,21 @@ +--- +name: Documentation problem +about: A typo, inaccuracy, or improvement in the documentation +title: '' +labels: documentation +assignees: '' + +--- + +## Documentation link + + + +## Describe the problem + + + + +## Suggested fix + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..63cb222 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,29 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + +### Is your feature request related to a problem? + + + + +### Describe the solution you'd like + + + + +### Describe alternatives you've considered + + + + +### Additional context + + + + \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..adee0ed --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" \ No newline at end of file diff --git a/.github/do-release.sh b/.github/do-release.sh new file mode 100644 index 0000000..2e0dffa --- /dev/null +++ b/.github/do-release.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +ansible-galaxy collection build +ansible-galaxy collection publish zscaler-zpacloud-* --server release_galaxy +ansible-galaxy collection publish zscaler-zpacloud-* --server automation_hub \ No newline at end of file diff --git a/.github/publishing.md b/.github/publishing.md new file mode 100644 index 0000000..5c6598a --- /dev/null +++ b/.github/publishing.md @@ -0,0 +1,18 @@ +# Publishing to AutomationHub and Galaxy + +## Publishing Process + +Publishing to both AutomationHub and Galaxy is done with the same methodology per destination, but with different targets. An ```ansible.cfg``` file is used to define the targets and per-target parameters. Currently, the release process performed by GitHub Actions creates this ```ansible.cfg``` file on the fly, injecting sensitive values from GitHub secrets as required. The current sensitive values are stored in corporate vault, and the values can be sourced/refreshed from [the AutomationHub API token page](https://console.redhat.com/ansible/automation-hub/token): +- AUTOMATION_HUB_API_TOKEN -> "Load token" button on the page +- AUTOMATION_HUB_URL -> Server URL value on the page +- AUTOMATION_HUB_SSO_URL -> SSO URL value on the page + +With the ```ansible.cfg``` file created and populated with the relevant values, GitHub Actions continues to publish to both Galaxy and AutomationHub, with two separate ```ansible-galaxy collection publish``` commands. + +## Keeping the API Token Active + +Inactive AutomationHub API tokens last for 30 days. In order to keep the token live between releases, a command described [here](https://console.redhat.com/ansible/automation-hub/token) should be executed. This command is currently executed by GitHub Actions on a regular schedule, and requires the current API token be passed as a parameter. + +## Documentation/References +- https://docs.ansible.com/ansible/latest/galaxy/user_guide.html#configuring-the-ansible-galaxy-client +- https://access.redhat.com/documentation/en-us/red_hat_ansible_automation_platform/1.2/html/getting_started_with_red_hat_ansible_automation_hub/index diff --git a/.github/set-version.sh b/.github/set-version.sh new file mode 100644 index 0000000..420ce99 --- /dev/null +++ b/.github/set-version.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +SCRIPT_BASE="$(cd "$( dirname "$0")" && pwd )" +ROOT=${SCRIPT_BASE}/.. + +# Exit immediatly if any command exits with a non-zero status +set -e + +# Usage +print_usage() { + echo "Set the app/add-on version" + echo "" + echo "Usage:" + echo " set-version.sh " + echo "" +} + +# if less than one arguments supplied, display usage +if [ $# -lt 1 ] +then + print_usage + exit 1 +fi + +# check whether user had supplied -h or --help . If yes display usage +if [ "$1" == "--help" ] || [ "$1" == "-h" ]; then + print_usage + exit 0 +fi + +# NEW_VERSION=$(echo "$1" | sed -e 's/-beta\./.b/' | sed -e 's/-alpha\./.a/') + +# Set version in galaxy.yml +grep -E '^version: (.+)$' "$ROOT/galaxy.yml" >/dev/null +sed -i.bak -E "s/^version: (.+)$/version: $1/" "$ROOT/galaxy.yml" && rm "$ROOT/galaxy.yml.bak" + +# Set version in docs/source/index.rst +grep -E '^Version: (.+)$' "$ROOT/docs/source/index.rst" > /dev/null +sed -i.bak -E "s/^Version: (.+)$/Version: $1/" "$ROOT/docs/source/index.rst" && rm "$ROOT/docs/source/index.rst.bak" + +# Set version in pyproject.toml +grep -E '^version = ".+"$' "$ROOT/pyproject.toml" >/dev/null +sed -i.bak -E "s/^version = \".+\"$/version = \"$1\"/" "$ROOT/pyproject.toml" && rm "$ROOT/pyproject.toml.bak" diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..333aa42 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,229 @@ +name: CI + +on: + push: + branches: + - master + - develop + pull_request: + #schedule: + # - cron: '0 6 * * *' + +env: + NAMESPACE: zscaler + COLLECTION_NAME: zpacloud + PYTHON_VERSION: 3.8 + +jobs: + + ## Sanity is required: + # + # https://docs.ansible.com/ansible/latest/dev_guide/testing_sanity.html + sanity: + name: Sanity (Ⓐ${{ matrix.ansible }}) + strategy: + matrix: + ansible: + - "2.9" + - "2.10" + - "2.11" + - "2.12" + include: + - sanity: "new-sanity" + - ansible: "2.9" + sanity: "old-sanity" + - ansible: "2.10" + sanity: "old-sanity" + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./ansible_collections/${{ env.NAMESPACE }}/${{ env.COLLECTION_NAME }} + steps: + - uses: actions/checkout@v3 + with: + path: ./ansible_collections/${{ env.NAMESPACE }}/${{ env.COLLECTION_NAME }} + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: ${{ env.PYTHON_VERSION }} + + - name: Install Poetry + uses: Gr1N/setup-poetry@v8 + #with: + # poetry-version: 1.0.10 + + # Install the head of the given branch (devel, stable-2.10) + - name: Install ansible-base (${{ matrix.ansible }}) + run: poetry run pip install https://github.com/ansible/ansible/archive/stable-${{ matrix.ansible }}.tar.gz --disable-pip-version-check + + - name: Create lock file + run: poetry lock + + #- name: Cache poetry dependencies + # uses: actions/cache@v2 + # with: + # #path: ~/.cache/pypoetry/virtualenvs + # #key: ${{ runner.os }}-poetry-${{ hashFiles('poetry.lock') }} + # ##restore-keys: | + # ## ${{ runner.os }}-poetry-${{ matrix.python-version }}- + # path: ${{ steps.poetry-cache.outputs.dir }} + # key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }} + # restore-keys: | + # ${{ runner.os }}-poetry- + + - name: Install dependencies + run: poetry install + + - name: Run sanity tests + timeout-minutes: 8 + run: poetry run make ${{ matrix.sanity }} + + format: + name: Code Format Check + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./ansible_collections/${{ env.NAMESPACE }}/${{ env.COLLECTION_NAME }} + steps: + - uses: actions/checkout@v3 + with: + path: ./ansible_collections/${{ env.NAMESPACE }}/${{ env.COLLECTION_NAME }} + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: ${{ env.PYTHON_VERSION }} + + - name: Install Poetry + uses: Gr1N/setup-poetry@v8 + + - name: Install dependencies + run: poetry install + + - name: Do black code format check + run: poetry run make check-format + + release: + name: release + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + needs: [sanity] + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + # This task could be removed once the task below is confirmed working + - name: Set up Galaxy auth + run: | + mkdir -p ~/.ansible + echo "token: $GALAXY_API_KEY" > ~/.ansible/galaxy_token + env: + GALAXY_API_KEY: ${{ secrets.GALAXY_API_KEY }} + shell: bash + + # New task for combined Galaxy and AutomationHub publishing + - name: Set up Automation Hub and Galaxy ansible.cfg file + run: | + cat << EOF > ansible.cfg + [galaxy] + server_list = automation_hub, release_galaxy + [galaxy_server.automation_hub] + url=${{ secrets.AUTOMATION_HUB_URL }} + auth_url=${{ secrets.AUTOMATION_HUB_SSO_URL }} + token=${{ secrets.AUTOMATION_HUB_API_TOKEN }} + [galaxy_server.release_galaxy] + url=https://galaxy.ansible.com/ + token=${{ secrets.GALAXY_API_KEY }} + EOF + shell: bash + + - name: Create release and publish + id: release + uses: cycjimmy/semantic-release-action@v2 + with: + semantic_version: 17.1.1 + extra_plugins: | + conventional-changelog-conventionalcommits@^4.4.0 + @semantic-release/changelog@^5.0.1 + @semantic-release/git@^9.0.0 + @semantic-release/exec@^5.0.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Store built collection + uses: actions/upload-artifact@v3 + with: + name: collection + path: | + *.tar.gz + + docs: + name: docs + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + needs: [release] + runs-on: ubuntu-latest + + defaults: + run: + working-directory: ./ansible_collections/zscaler/zpacloud + + steps: + # Just a note here: The Ansible stuff is apparently doing realpath + # checks, so trying to simlink stuff and then run Ansible commands + # such as ansible-test in the symlink directory fails. Thus we need + # to have the real path contain ansible_collections/zscaler/zpacloud. + - name: Checkout + uses: actions/checkout@v3 + with: + path: ./ansible_collections/zscaler/zpacloud + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: 3.9 + + - name: Install Poetry + uses: Gr1N/setup-poetry@v8 + + - name: Add ansible-core + run: poetry add ansible-core^2.13 + + - name: Add antsibull-docs + run: poetry add antsibull-docs + + - name: Install dependencies + run: poetry install + + - name: Build the collection + run: poetry run ansible-galaxy collection build + + # - name: Download built collection + # uses: actions/download-artifact@v2 + # with: + # name: collection + + - name: Install built collection + run: poetry run ansible-galaxy collection install *.tar.gz + + - name: Generate documentation + run: poetry run make docs + + # This is here for right now because the action to deploy seems to assume + # (and not have a configuration option to) mirror the actions/checkout@v3 + # the with.path spec. + - name: Move the repo to where the deploy action is looking for it + run: | + cd ../../../.. + mv zpacloud-ansible the_repo + mv the_repo/ansible_collections/zscaler/zpacloud zpacloud-ansible + mkdir -p zpacloud-ansible/ansible_collections/zscaler/zpacloud + + - name: Deploy to GitHub Pages + uses: JamesIves/github-pages-deploy-action@v4.4.1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + branch: gh-pages + folder: docs/html + clean: true diff --git a/.github/workflows/automationhub_token_keepalive.yml b/.github/workflows/automationhub_token_keepalive.yml new file mode 100644 index 0000000..583ea99 --- /dev/null +++ b/.github/workflows/automationhub_token_keepalive.yml @@ -0,0 +1,19 @@ +--- +name: AutomationHub Token Keepalive +# Inactive tokens expire after 30 days, this will stop the 30 day timer with regular check-ins + +"on": + schedule: + - cron: '0 0 1,15 * *' # Scheduled for 00:00 on day 1 and day 15 of every month + +jobs: + automationhub_token_keepalive: + runs-on: "ubuntu-latest" + steps: + - name: "Execute keepalive command" + run: | + curl ${{ secrets.AUTOMATION_HUB_SSO_URL }} \ + -d grant_type=refresh_token \ + -d client_id="cloud-services" \ + -d refresh_token="${{ secrets.AUTOMATION_HUB_API_TOKEN }}" \ + --fail --silent --show-error --output /dev/null diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..44479f2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,114 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg +.DS_Store + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# IPython Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# dotenv +.env + +# virtualenv +.venv/ +venv/ +ENV/ +.envrc + +# PyCharm / IntelliJ +.idea + +# VS Code +.vscode + +# Configtree diagram generated by sphinx +docs/_diagrams + +# vim swap files +*.swp +/_build +/doctrees + +# Runtime files +*.retry +*.swp +__pycache__ +*.pyc + +tests/output/ + +docs/html/ +docs/source/_build/ +docs/source/modules/ + +tests/integration/inventory +tests/integration/*.log + +*.tar.gz +examples/script.sh +collections_test/ +zpa_all_together/ +zpa_all_together/* +scripts/ +scripts/* +ansible_collections/ +ansible_demo/ +tests/integration/integration_config.yml +local_test \ No newline at end of file diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 0000000..ff5361f --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,7 @@ +[settings] +multi_line_output = 3 +include_trailing_comma = true +force_grid_wrap = 0 +use_parentheses = true +ensure_newline_before_comments = true +line_length = 88 \ No newline at end of file diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/.releaserc.json b/.releaserc.json new file mode 100644 index 0000000..cea237a --- /dev/null +++ b/.releaserc.json @@ -0,0 +1,45 @@ +{ + "branches": [ + "master" + ], + "plugins": [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + [ + "@semantic-release/exec", + { + "prepareCmd": ".github/set-version.sh ${nextRelease.version}", + "publishCmd": ".github/do-release.sh" + } + ], + [ + "@semantic-release/changelog", + { + "changelogFile": "./CHANGELOG.md" + } + ], + [ + "@semantic-release/git", + { + "assets": [ + "galaxy.yml", + "docs/source/index.rst", + "./CHANGELOG.md" + ], + "message": "chore(release): ${nextRelease.version}\n\n${nextRelease.notes}" + } + ], + [ + "@semantic-release/github", + { + "assets": [ + { + "path": "*.tar.gz" + } + ], + "successComment": ":tada: This ${issue.pull_request ? 'PR is included' : 'issue has been resolved'} in version ${nextRelease.version} :tada:\n\nThe release is available on [Ansible Galaxy](https://galaxy.ansible.com/zscaler/zpacloud) and [GitHub release](https://github.com/zscaler/zpacloud-ansible/releases)\n\n> Posted by [semantic-release](https://github.com/semantic-release/semantic-release) bot" + } + ] + ], + "preset": "conventionalcommits" +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e69de29 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..97536e0 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at github@securitygeek.io. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1916ff2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 [Zscaler](https://github.com/zscaler) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..97d6cae --- /dev/null +++ b/Makefile @@ -0,0 +1,69 @@ +# Taken from: https://github.com/sensu/sensu-go-ansible/blob/master/Makefile + +# Make sure we have ansible_collections/zscaler/zpacloud_enhanced +# as a prefix. This is ugly as heck, but it works. I suggest all future +# developer to treat next few lines as an opportunity to learn a thing or two +# about GNU make ;) +collection := $(notdir $(realpath $(CURDIR) )) +namespace := $(notdir $(realpath $(CURDIR)/.. )) +toplevel := $(notdir $(realpath $(CURDIR)/../..)) + +err_msg := Place collection at /ansible_collections/zscaler/zpacloud +ifneq (zpacloud,$(collection)) + $(error $(err_msg)) +else ifneq (zscaler,$(namespace)) + $(error $(err_msg)) +else ifneq (ansible_collections,$(toplevel)) + $(error $(err_msg)) +endif + +python_version := $(shell \ + python3 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))' \ +) + + +.PHONY: help +help: + @echo Available targets: + @fgrep "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sort + +.PHONY: docs +docs: ## Build collection documentation + mkdir antsibull + poetry run antsibull-docs collection --use-current --dest-dir antsibull --no-indexes collections zscaler.zpacloud + mkdir -p docs/source/modules + mv antsibull/collections/zscaler/zpacloud/* docs/source/modules + rm -rf antsibull + rm -f docs/source/modules/index.rst + cd docs && sphinx-build source html + +.PHONY: clean +clean: ## Remove all auto-generated files + rm -rf tests/output + rm -rf *.tar.gz + +.PHONY: format +format: ## Format with black + black . + +.PHONY: check-format +check-format: ## Check with black + black --check --diff . + +.PHONY: old-sanity +old-sanity: ## Sanity tests for Ansible v2.9 and Ansible v2.10 + ansible-test sanity -v --skip-test pylint --skip-test rstcheck --python $(python_version) + +.PHONY: new-sanity +new-sanity: ## Sanity tests for Ansible v2.11 and above + ansible-test sanity -v --skip-test pylint --python $(python_version) + +.PHONY: reqs +reqs: ## Recreate the requirements.txt file + poetry export -f requirements.txt --output requirements.txt + +install: + rm -f zscaler* + ansible-galaxy collection build . --force + ansible-galaxy collection install zscaler* --force + rm -f zscaler* \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..97ecde8 --- /dev/null +++ b/README.md @@ -0,0 +1,160 @@ +# Zscaler Private Access (ZPA) Ansible Collection + +![Version on Galaxy](https://img.shields.io/badge/dynamic/json?style=flat&label=Ansible+Galaxy&prefix=v&url=https://galaxy.ansible.com/api/v2/collections/zscaler/zpacloud/&query=latest_version.version) +[![sanity](https://github.com/zscaler/zpacloud-ansible/actions/workflows/ansible-test-sanity.yml/badge.svg?branch=master)](https://github.com/zscaler/zpacloud-ansible/actions/workflows/ansible-test-sanity.yml) +[![integration](https://github.com/zscaler/zpacloud-ansible/actions/workflows/ansible-test-integration.yml/badge.svg?branch=master)](https://github.com/zscaler/zpacloud-ansible/actions/workflows/ansible-test-integration.yml) +[![CI](https://github.com/zscaler/zpacloud-ansible/actions/workflows/CI.yml/badge.svg)](https://github.com/zscaler/zpacloud-ansible/actions/workflows/CI.yml) + +This collection contains modules and plugins to assist in automating the configuration and operational tasks on Zscaler Private Access cloud, and API interactions with Ansible. + +- Free software: MIT License +- [Documentation](https://zscaler.github.io/zpacloud-ansible) +- [Repository](https://github.com/zscaler/zpacloud-ansible) +- [Example Playbooks](https://github.com/zscaler/zpacloud-playbooks) + +## Tested Ansible Versions + +This collection is tested with the most current Ansible 2.9 and 2.10 releases. Ansible versions +before 2.9.10 are **not supported**. + +## Included content + +- [zpa_app_connector_groups](https://zscaler.github.io/zpacloud-ansible/modules/zpa_app_connector_groups.html) - Create/Update/Delete an app connector group. +- [zpa_app_connector_groups_info](https://zscaler.github.io/zpacloud-ansible/modules/zpa_app_connector_groups_info.html) - Gather information details (ID and/or Name) of a app connector group. +- [zpa_application_segment](https://zscaler.github.io/zpacloud-ansible/modules/zpa_application_segment.html) - Create/Update/Delete an application segment. +- [zpa_application_segment_info](https://zscaler.github.io/zpacloud-ansible/modules/zpa_application_segment_info.html) - Gather information details (ID and/or Name) of a application segment. +- [zpa_application_server](https://zscaler.github.io/zpacloud-ansible/modules/zpa_application_server.html) - Create/Update/Delete an Application Server. +- [zpa_application_server_info](https://zscaler.github.io/zpacloud-ansible/modules/zpa_application_server_info.html) - Gather information details (ID and/or Name) of an application server. +- [zpa_ba_certificate_info](https://zscaler.github.io/zpacloud-ansible/modules/zpa_ba_certificate_info.html) - Gather information details (ID and/or Name) of an browser access certificate. +- [zpa_cloud_connector_group_info](https://zscaler.github.io/zpacloud-ansible/modules/zpa_cloud_connector_group_info.html) - Gather information details (ID and/or Name) of an cloud connector group. +- [zpa_customer_version_profile_info](https://zscaler.github.io/zpacloud-ansible/modules/zpa_customer_version_profile_info.html) - Gather information details (ID and/or Name) of an customer version profile for use in app connector group resource in the `version_profile_id` parameter. +- [zpa_enrollment_cert_info](https://zscaler.github.io/zpacloud-ansible/modules/zpa_enrollment_cert_info.html) - Gather information details (ID and/or Name) of an enrollment certificate for use when creating provisioning keys for connector groups or service edge groups. +- [zpa_idp_controller_info](https://zscaler.github.io/zpacloud-ansible/modules/zpa_idp_controller_info.html) - Gather information details (ID and/or Name) of an identity provider (IdP) created in the ZPA tenant. +- [zpa_machine_group_info](https://zscaler.github.io/zpacloud-ansible/modules/zpa_machine_group_info.html) - Gather information details (ID and/or Name) of an machine group for use in a policy access and/or forwarding rules. +- [zpa_policy_access_rule](https://zscaler.github.io/zpacloud-ansible/modules/zpa_policy_access_rule.html) - Create/Update/Delete a policy access rule. +- [zpa_policy_access_rule_info](https://zscaler.github.io/zpacloud-ansible/modules/zpa_policy_access_rule_info.html) - Gather information details (ID and/or Name) of a policy access rule. +- [zpa_policy_timeout_rule](https://zscaler.github.io/zpacloud-ansible/modules/zpa_policy_timeout_rule.html) - Create/Update/Delete a policy access timeout rule. +- [zpa_policy_timeout_rule_info](https://zscaler.github.io/zpacloud-ansible/modules/zpa_policy_timeout_rule_info.html) - Gather information details (ID and/or Name) of a policy access timeout rule. +- [zpa_policy_forwarding_rule](https://zscaler.github.io/zpacloud-ansible/modules/zpa_policy_forwarding_rule.html) - Create/Update/Delete a policy access forwarding rule. +- [zpa_policy_forwarding_rule_info](https://zscaler.github.io/zpacloud-ansible/modules/zpa_policy_forwarding_rule_info.html) - Gather information details (ID and/or Name) of a policy access forwarding rule. +- [zpa_posture_profile_info](https://zscaler.github.io/zpacloud-ansible/modules/zpa_posture_profile_info.html) - Gather information details (ID and/or Name) of a posture profile to use in a policy access, timeout or forwarding rules. +- [zpa_provisioning_key](https://zscaler.github.io/zpacloud-ansible/modules/zpa_provisioning_key.html) - Create/Update/Delete a provisioning key. +- [zpa_provisioning_key_info](https://zscaler.github.io/zpacloud-ansible/modules/zpa_provisioning_key_info.html) - Gather information details (ID and/or Name) of a provisioning key. +- [zpa_saml_attribute_info](https://zscaler.github.io/zpacloud-ansible/modules/zpa_saml_attribute_info.html) - Gather information details (ID and/or Name) of a saml attribute. +- [zpa_scim_attribute_header_info](https://zscaler.github.io/zpacloud-ansible/modules/zpa_scim_attribute_header_info.html) - Gather information details (ID and/or Name) of a scim attribute header. +- [zpa_scim_group_info](https://zscaler.github.io/zpacloud-ansible/modules/zpa_scim_group_info.html) - Gather information details (ID and/or Name) of a scim group. +- [zpa_segment_group](https://zscaler.github.io/zpacloud-ansible/modules/zpa_segment_group.html) - Create/Update/Delete a segment group. +- [zpa_segment_group_info](https://zscaler.github.io/zpacloud-ansible/modules/zpa_segment_group_info.html) - Gather information details (ID and/or Name) of a segment group. +- [zpa_server_group](https://zscaler.github.io/zpacloud-ansible/modules/zpa_server_group.html) - Create/Update/Delete a segment group. +- [zpa_server_group_info](https://zscaler.github.io/zpacloud-ansible/modules/zpa_server_group_info.html) - Gather information details (ID and/or Name) of a server group. +- [zpa_service_edge_group_info](https://zscaler.github.io/zpacloud-ansible/modules/zpa_service_edge_group_info.html) - Gather information details (ID and/or Name) of a service edge group. +- [zpa_service_edge_group](https://zscaler.github.io/zpacloud-ansible/modules/zpa_service_edge_group.html) - Create/Update/Delete an service edge group. +- [zpa_trusted_network_info](https://zscaler.github.io/zpacloud-ansible/modules/zpa_trusted_network_info.html) - Gather information details (ID and/or Name) of a trusted network for use in a policy access and/or forwarding rules. + +## Installation and Usage + +Before using the ZPACloud collection, you need to install it with the Ansible Galaxy CLI: + +```bash +ansible-galaxy collection install zscaler.zpacloud +``` + +You can also include it in a `requirements.yml` file and install it via `ansible-galaxy collection install -r requirements.yml`, using the format: + +```yaml + collections: + - zscaler.zpacloud +``` + +### Using modules from the ZPACloud Collection in your playbooks + +It's preferable to use content in this collection using their Fully Qualified Collection Namespace (FQCN), for example `zscaler.zpacloud.zpa_app_connector_groups`: + +```yaml +--- +- hosts: localhost + gather_facts: false + connection: local + + tasks: + - name: Get Information Details of All Customer Version Profiles + zscaler.zpacloud.zpa_customer_version_profile_info: + register: version_profile_id + + - name: Create App Connector Group Example + zscaler.zpacloud.zpa_app_connector_groups: + name: "Example" + description: "Example" + enabled: true + city_country: "California, US" + country_code: "US" + latitude: "37.3382082" + longitude: "-121.8863286" + location: "San Jose, CA, USA" + upgrade_day: "SUNDAY" + upgrade_time_in_secs: "66600" + override_version_profile: true + version_profile_id: "{{ version_profile_id.data[0].id }}" + dns_query_type: "IPV4" +``` + +If you are using versions prior to Ansible 2.10 and this collection's existence, you can also define `collections` in your play and refer to this collection's modules as you did in Ansible 2.9 and below, as in this example: + +```yaml +--- +- hosts: localhost + gather_facts: false + connection: local + + collections: + - zscaler.zpacloud + + tasks: + - name: Get Information Details of All Customer Version Profiles + zpa_customer_version_profile_info: + register: version_profile_id + + - name: Create App Connector Group Example + zpa_app_connector_groups: + name: "Example" + description: "Example" + enabled: true + city_country: "California, US" + country_code: "US" + latitude: "37.3382082" + longitude: "-121.8863286" + location: "San Jose, CA, USA" + upgrade_day: "SUNDAY" + upgrade_time_in_secs: "66600" + override_version_profile: true + version_profile_id: "{{ version_profile_id.data[0].id }}" + dns_query_type: "IPV4" + ... +``` + +License +======== + +MIT License + +======= + +Copyright (c) 2023 [Zscaler](https://github.com/zscaler) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/SUPPORT.md b/SUPPORT.md new file mode 100644 index 0000000..f21e1ee --- /dev/null +++ b/SUPPORT.md @@ -0,0 +1,10 @@ +# Community Supported + +The software and templates in this repository are released under an as-is, best effort, +support policy. This software should be seen as community supported and Zscaler's Technology Alliances +will contribute our expertise as and when possible. +We do not provide technical support or help in using or troubleshooting the components of the project through our normal support options such as Zscaler support teams, or ASC (Authorized Support Centers) partners and backline support options. The underlying product uses (Zscaler Private Access API) by the scripts or templates are still supported, but the support is only for the product functionality and not for help in deploying or using the template or script itself. Unless explicitly tagged, all projects or work posted in our [GitHub repository](https://github.com/zscaler) or sites other than our official [Downloads page](https://support.zscaler.com) are provided under the best effort policy. + +As of version 2.13.0, this Collection of Ansible Modules for Zscaler Private Access (ZPA) is +[certified on Ansible Automation Hub](https://console.redhat.com/ansible/automation-hub/repo/published/zscaler/zpacloud) and officially supported for Ansible subscribers. Ansible subscribers can engage +for support through their usual route towards Red Hat. diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 0000000..e48df8b --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1,144 @@ +# Contributing + +Contributions are welcome, and they are greatly appreciated! Every little bit helps, +and credit will always be given. + +You can contribute in many ways: + +## Types of Contributions + +### Report Bugs + +Report bugs at https://github.com/zscaler/zpacloud-ansible/issues + +### Fix Bugs + +Look through the GitHub issues for bugs. Anything tagged with "bug" is open to whoever +wants to fix it. + +### Implement Features + +Look through the GitHub issues for features. Anything tagged with "enhancement" is +open to whoever wants to implement it. + +### Submit Feedback + +The best way to send feedback is to file an issue at +https://github.com/zscaler/zpacloud-ansible/issues. + +If you are proposing a feature: + +- Explain in detail how it would work. +- Keep the scope as narrow as possible, to make it easier to implement. +- Remember that this is a volunteer-driven project, and that contributions + are welcome :) + +## Get Started! + +Ready to contribute some code? Here's how to set up `zpacloud-ansible` for local development. + +1. Install Python 3.6 or higher, along with Ansible + + Development must be done using Python 3.6 or higher. Ansible still technically + supports Python 2.7, but all code should target Python 3.6 or higher. + +2. Fork the `zpacloud-ansible` repo on GitHub. + +3. Create a top level directory for your work, for example `ansible-hacking`: + +``` +$ mkdir ansible-hacking +$ cd ansible-hacking/ +``` + +3. Clone your fork locally, using a special directory name so that Ansible understands + it as a collection: + +``` +$ mkdir -p ansible_collections/zscaler +$ git clone https://github.com/your-username/zpacloud-ansible.git ansible_collections/zscaler/zpacloud +``` + +4. Create a playbooks directory, and add our top level directory to `ansible.cfg`: + + Adding our top level directory to `ansible.cfg` will interpret the directory + `ansible_collections/zscaler/zpacloud` as the collection + `zscaler.zpacloud` without us having to build and install the collection each + time! + + You can add any test playbooks to the `playbooks/` directory. Any + `ansible-playbook` runs from this directory will pick up our custom `ansible.cfg` + file. + +``` +$ mkdir playbooks +$ echo "[defaults]\ncollections_paths = .." > playbooks/ansible.cfg +``` + +5. Create a branch for local development + +```sh +$ cd ansible_collections/zscaler/zpacloud +$ git checkout -b name-of-your-bugfix-or-feature +``` + +6. Now you can make your changes locally, and test them out by running +`ansible-playbook` from the `playbooks/` directory. + +7. When you're done making changes, check that your changes pass `ansible-test sanity`: + +``` +$ ansible-test sanity --local +``` + +8. Commit your changes and push your branch to GitHub: + +``` +$ git add -A +$ git commit -m "Your detailed description of your changes." +$ git push origin name-of-your-bugfix-or-feature +``` + +9. Submit a pull request through the GitHub website. + +## Publish a new release (for maintainers) + +This workflow requires node, npm, and semantic-release to be installed locally: + +```sh +$ npm install -g semantic-release@^17.1.1 @semantic-release/git@^9.0.0 @semantic-release/exec@^5.0.0 conventional-changelog-conventionalcommits@^4.4.0 +``` + +### Test the release process + +Run `semantic-release` on develop: + +```sh +semantic-release --dry-run --no-ci --branches=develop +``` + +Verify in the output that the next version is set correctly, and the release notes are generated correctly. + +### Merge develop to main and push + +```sh +git checkout main +git merge develop +git push origin main +``` + +At this point, GitHub Actions builds the final release, and uploads it to Ansible Galaxy. + +### Merge main to develop and push + +Now, sync develop to main to add the new commits made by the release bot. + +```sh +git fetch --all --tags +git pull origin main +git checkout develop +git merge main +git push origin develop +``` + +Now you're ready to branch again and work on the next feature. diff --git a/docs/history.md b/docs/history.md new file mode 100644 index 0000000..e2b8c74 --- /dev/null +++ b/docs/history.md @@ -0,0 +1,10 @@ +Release History +=============== + +v1.0.0 +------ + +- *Released*: 2022-02-01 + +This is the initial version of the `zpacloud` collection. This is a straight port +of v1.0.0. diff --git a/docs/source/authors.rst b/docs/source/authors.rst new file mode 100644 index 0000000..04b41eb --- /dev/null +++ b/docs/source/authors.rst @@ -0,0 +1,14 @@ +======= +Authors +======= + +Development Leads +----------------- + +- William Guilherme (@willguibr) + +Contributors +------------ + +Credits +------- diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 0000000..d0ec9c6 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,116 @@ +# -*- coding: utf-8 -*- +# +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +import os +import sys + +sys.path.insert(0, os.path.abspath("../../plugins/modules")) + +# -- Project information ----------------------------------------------------- + +project = "Zscaler Private Access Ansible Collection" +copyright = "2022, William Guilherme" +author = "William Guilherme" +html_title = "" + +# The short X.Y version +version = "1.0" +# The full version, including alpha/beta/rc tags +release = "1.0.0" + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + "sphinx.ext.intersphinx", + "sphinx.ext.todo", + "sphinx.ext.githubpages", + "sphinx.ext.napoleon", + "sphinx_rtd_theme", +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = ".rst" + +# The master toctree document. +master_doc = "index" + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = None + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "sphinx_rtd_theme" +html_context = { + "display_github": True, + "github_user": "willguibr", + "github_repo": "zpacloud-ansible", + "github_version": "master", + "conf_py_path": "/docs/source/", +} + + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = { +# "sidebar_hide_name": True, +# } + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# The default sidebars (for documents that don't match any pattern) are +# defined by theme itself. Builtin themes are using these templates by +# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', +# 'searchbox.html']``. +# +# html_sidebars = {} diff --git a/docs/source/examples.rst b/docs/source/examples.rst new file mode 100644 index 0000000..6e1ebc9 --- /dev/null +++ b/docs/source/examples.rst @@ -0,0 +1,254 @@ +======== +Examples +======== + +What is Zscaler Private Access +============================== + +The Zscaler Private Access (ZPA) service enables organizations to provide access to internal applications and services while ensuring the security of their networks. +ZPA is an easier to deploy, more cost-effective, and more secure alternative to VPNs. Unlike VPNs, which require users to connect to your network to access your enterprise applications, +ZPA allows you to give users policy-based secure access only to the internal apps they need to get their work done. With ZPA, application access does not require network access. + +App Connector Group +=================== + +The following module allows for interaction with the ZPA App Connector Group API endpoints. +This module creates an app connector group, which in turn must be associated with a provisioning key resource. + +.. code-block:: yaml + + - name: Create First App Connector Group + zscaler.zpacloud.zpa_app_connector_groups: + name: "Example1" + description: "Example1" + enabled: true + city_country: "California, US" + country_code: "US" + latitude: "37.3382082" + longitude: "-121.8863286" + location: "San Jose, CA, USA" + upgrade_day: "SUNDAY" + upgrade_time_in_secs: "66600" + override_version_profile: true + version_profile_id: "0" + dns_query_type: "IPV4" + +Service Edge Group +================== + +The following module allows for interaction with the ZPA Service Edge Group API endpoints. +This module creates an service edge group, which in turn must be associated with a provisioning key resource. + +.. code-block:: yaml + + - name: Create/Update/Delete Service Edge Group + zscaler.zpacloud.zpa_service_edge_groups: + name: "Example" + description: "Example1" + enabled: true + city_country: "California, US" + country_code: "US" + latitude: "37.3382082" + longitude: "-121.8863286" + location: "San Jose, CA, USA" + upgrade_day: "SUNDAY" + upgrade_time_in_secs: "66600" + override_version_profile: true + version_profile_id: "0" + +Provisioning Key +================ + +The following module allows for interaction with the ZPA Provisioning Key API endpoints. +This module creates a provisioning key resource, which is a text string that is generated when a new App Connector +or Private Service Edge is added. + +.. code-block:: yaml + + - name: Create/Update/Delete App Connector Group Provisioning Key + zscaler.zpacloud.zpa_provisioning_key: + name: "App Connector Group Provisioning Key" + association_type: "CONNECTOR_GRP" + max_usage: "10" + enrollment_cert_id: 6573 + zcomponent_id: 216196257331291903 + + - name: Create/Update/Delete Service Edge Connector Group Provisioning Key + zscaler.zpacloud.zpa_provisioning_key: + name: "Service Edge Connector Group Provisioning Key" + association_type: "CONNECTOR_GRP" + max_usage: "10" + enrollment_cert_id: 6573 + zcomponent_id: 216196257331291903 + + +Application Segment +=================== + +The following module allows for interaction with the ZPA Application Segments endpoints. +The module creates an application segment resource, which is a grouping of defined applications. + +.. code-block:: yaml + + - name: Create First Application Segment + zscaler.zpacloud.zpa_application_segment: + name: Example Application + description: Example Application Test + enabled: true + health_reporting: ON_ACCESS + bypass_type: NEVER + is_cname_enabled: true + tcp_port_range: + - from: "8080" + to: "8085" + domain_names: + - server1.example.com + - server2.example.com + segment_group_id: "{{ segment_group_id }}" + server_groups: + - id: "{{ server_group_id }}" + +Browser Access Application Segment +================================== + +The following module allows for interaction with the ZPA Application Segments endpoints. +The module creates a Browser Access Application Segment resource, which allows you to leverage +a web browser for user authentication and application access over ZPA, without requiring users +to install the Zscaler Client Connector (formerly Zscaler App or Z App) on their devices. + +.. code-block:: yaml + + - name: Browser Access Application Segment + zscaler.zpacloud.zpa_browser_access: + name: Example + description: Example + enabled: true + health_reporting: ON_ACCESS + bypass_type: NEVER + is_cname_enabled: true + tcp_port_range: + - from: "80" + to: "80" + domain_names: + - crm1.example.com + - crm2.example.com + segment_group_id: "{{ segment_group_id }}" + server_groups: + - id: "{{ server_group_id }}" + clientless_apps: + name: "sales.acme.com" + application_protocol: "HTTP" + application_port: "80" + certificate_id: "{{ certificate_id }}" + trust_untrusted_cert: true + enabled: true + domain: "sales.acme.com" + +Server Group +============ + +The following module allows for interaction with the ZPA Server Groups endpoints. +The module creates a Server Group resource, which can be created to manually define servers, +or it can be created with the option of `dynamic_discovery` enabled so that ZPA discovers the appropriate servers, +for each application as users request them. + +.. code-block:: yaml + + - name: Create/Update/Delete a Server Group (Dynamic Discovery ON) + zscaler.zpacloud.zpa_server_group: + name: "Example" + description: "Example" + enabled: false + dynamic_discovery: true + app_connector_groups: + - id: "216196257331291924" + + - name: Create/Update/Delete a Server Group (Dynamic Discovery OFF) + zscaler.zpacloud.zpa_server_group: + name: "Example" + description: "Example" + enabled: false + dynamic_discovery: false + app_connector_groups: + - id: "216196257331291924" + servers: + - id: "216196257331291921" + +Segment Group +============= + +The following module allows for interaction with the ZPA Segment Groups endpoints. + +.. code-block:: yaml + + - name: Create/Update/Delete a Segment Groups + zscaler.zpacloud.zpa_segment_group: + config_space: "DEFAULT" + name: Example Segment Group + description: Example Segment Group + enabled: true + policy_migrated: true + tcp_keep_alive_enabled: "1" + +Policy Access Rule +================== + +.. code-block:: yaml + + - name: Create/update/delete a Policy Rule + zscaler.zpacloud.zpa_policy_access_rule: + name: "Example Policy Access Rule" + description: "Example Policy Access Rule" + action: "ALLOW" + rule_order: 1 + operator: "AND" + conditions: + - negated: false + operator: "OR" + operands: + - name: "Example Policy Access Rule" + object_type: "APP" + lhs: "id" + rhs: "216196257331291979" + +Policy Access Timeout Rule +========================== + +.. code-block:: yaml + + - name: Create/update/delete a Policy Timeout Rule + zscaler.zpacloud.zpa_policy_access_timeout_rule: + name: "Example Policy Timeout Rule" + description: "Example Policy Timeout Rule" + action: "RE_AUTH" + rule_order: 1 + operator: "AND" + conditions: + - negated: false + operator: "OR" + operands: + - name: "Application_Segment" + object_type: "APP" + lhs: "id" + rhs: "216196257331291979" + +Policy Access Forwarding Rule +============================= + +.. code-block:: yaml + + - name: Create/update/delete a Policy Forwarding Rule + zscaler.zpacloud.zpa_policy_access_forwarding_rule: + name: "Example Policy Forwarding Rule" + description: "Example Policy Forwarding Rule" + action: "BYPASS" + rule_order: 1 + operator: "AND" + conditions: + - negated: false + operator: "OR" + operands: + - name: "Application_Segment" + object_type: "APP" + lhs: "id" + rhs: "216196257331291979" \ No newline at end of file diff --git a/docs/source/history.rst b/docs/source/history.rst new file mode 100644 index 0000000..47f961a --- /dev/null +++ b/docs/source/history.rst @@ -0,0 +1,5 @@ +=============== +Release History +=============== + +For a complete release history, see the `GitHub releases page `_ for the repository. \ No newline at end of file diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 0000000..338b2b0 --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,53 @@ +========================================= +Zscaler Private Access Ansible Collection +========================================= + +Version: 1.2.0 + +The Zscaler Private Access Ansible collection is a collection of modules that +automate configuration and operational tasks on Zscaler Private Access Cloud. The +underlying protocol uses API calls that are wrapped within the Ansible +framework. + +This is a **community supported project**; hence, this project or the software module is not affiliated or supported by Zscaler engineering teams in any way. + +Installation +============ + +Ansible 2.9 is **required** for using collections. + +Install the collection using `ansible-galaxy`: + +.. code-block:: bash + + ansible-galaxy collection install zscaler.zpacloud + +Then in your playbooks you can specify that you want to use the +`zpacloud` collection like so: + +.. code-block:: yaml + + collections: + - zscaler.zpacloud + +* Ansible Galaxy: https://galaxy.ansible.com/zscaler/zpacloud +* GitHub repo: https://github.com/zscaler/zpacloud-ansible + + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + examples + modules + history + authors + license + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/source/license.rst b/docs/source/license.rst new file mode 100644 index 0000000..98f7849 --- /dev/null +++ b/docs/source/license.rst @@ -0,0 +1,5 @@ +======= +License +======= + +.. include:: ../../LICENSE diff --git a/docs/source/modules.rst b/docs/source/modules.rst new file mode 100644 index 0000000..394639e --- /dev/null +++ b/docs/source/modules.rst @@ -0,0 +1,8 @@ +Module reference +---------------- + +.. toctree:: + :glob: + :maxdepth: 1 + + modules/* \ No newline at end of file diff --git a/docs/templates/module.rst.j2 b/docs/templates/module.rst.j2 new file mode 100644 index 0000000..18171e8 --- /dev/null +++ b/docs/templates/module.rst.j2 @@ -0,0 +1,186 @@ +.. _{{ module }}_module: + +{% if short_description %} +{% set title = module + ' -- ' + short_description | rst_ify %} +{% else %} +{% set title = module %} +{% endif %} + +{{ title }} +{{ '=' * title|length }} + +.. contents:: + :local: + :depth: 1 + + +{% if description -%} +Synopsis +-------- + +{% for desc in description %} +{{ desc | rst_ify }} + +{% endfor %} +{% endif %} + +{% if deprecated %} +.. WARNING:: + This module is **deprecated**, and will be removed in version {{ deprecated['removed_in'] }} + of the collection. + + {{ deprecated['why'] }} + + {{ deprecated['alternative'] }} + +{% endif %} + + +{% if requirements -%} +Requirements +------------ +The below requirements are needed on the host that executes this module. + +{% for req in requirements %} +- {{ req | rst_ify }} +{% endfor %} +{% endif %} + + +{% macro option_desc(opts, level) %} +{% for name, spec in opts.items() %} +{% set req = spec.required %} +{% set typ = spec.type %} +{% set def_val = spec.default %} + {{ " " * level }}{{ name }} ({% if req %}required, {% endif %}type: {{ typ }}{% if def_val %}, default: {{ def_val }}{% endif %}) +{% for para in spec.description %} + {{ " " * level }}{{ para | rst_ify }} + +{% endfor %} + +{% if spec.suboptions %} +{{ option_desc(spec.suboptions, level + 1) }} +{% endif %} +{% endfor %} +{% endmacro %} + +{% if options -%} +Parameters +---------- + +{{ option_desc(options, 0) }} +{% endif %} + + +{% if notes -%} +Notes +----- + +.. note:: +{% for note in notes %} + - {{ note | rst_ify }} +{% endfor %} +{% endif %} + + +{% if seealso -%} +See Also +-------- + +.. seealso:: + +{% for item in seealso %} +{% if item.module is defined and item.description is defined %} + :ref:`{{ item.module }}_module` + {{ item.description | rst_ify }} +{% elif item.module is defined %} + :ref:`{{ item.module }}_module` + The official documentation on the **{{ item.module }}** module. +{% elif item.name is defined and item.link is defined and item.description is defined %} + `{{ item.name }} <{{ item.link }}>`_ + {{ item.description | rst_ify }} +{% elif item.ref is defined and item.description is defined %} + :ref:`{{ item.ref }}` + {{ item.description | rst_ify }} +{% endif %} +{% endfor %} +{% endif %} + + +{% if examples -%} +Examples +-------- + +.. code-block:: yaml+jinja + +{{ examples | indent(4, True) }} +{% endif %} + +{% macro result_desc(results, level) %} +{% for name, spec in results.items() %} +{% set ret = spec.returned %} +{% set typ = spec.type | default("any") %} +{% set sample = spec.sample %} +{{ " " * level }}{{ name }} ({{ ret }}, {{ typ }}, {{ sample }}) +{% for para in spec.description %} + {{ " " * level }}{{ para | rst_ify }} + +{% endfor %} + +{% if spec.contains %} +{{ result_desc(spec.contains, level + 1) }} +{% endif %} +{% endfor %} +{% endmacro %} + +{% if returndocs -%} +Return Values +------------- + +{{ result_desc(returndocs, 0) }} +{% endif %} + + +Status +------ + +{% if deprecated %} + +- This {{ plugin_type }} will be removed in version + {{ deprecated['removed_in'] | default('') | string | rst_ify }}. + *[deprecated]* + +{% else %} + +{% set module_states = { + "preview": "not guaranteed to have a backwards compatible interface", + "stableinterface": "guaranteed to have backward compatible interface changes going forward", + } +%} + +{% if metadata %} +{% if metadata.status %} + +{% for cur_state in metadata.status %} +- This {{ plugin_type }} is {{ module_states[cur_state] }}. *[{{ cur_state }}]* +{% endfor %} + +{% endif %} + +{% if metadata.supported_by %} +- This {{ plugin_type }} is maintained by {{ metadata.supported_by }}. +{% endif %} + +{% endif %} + +{% endif %} + +{% if author is defined -%} +Authors +~~~~~~~ + +{% for author_name in author %} +- {{ author_name }} +{% endfor %} + +{% endif %} \ No newline at end of file diff --git a/examples/zpa_app_connector_groups.yml b/examples/zpa_app_connector_groups.yml new file mode 100644 index 0000000..df13e77 --- /dev/null +++ b/examples/zpa_app_connector_groups.yml @@ -0,0 +1,69 @@ +--- +# zpa_app_connector_groups.yml - Create/Update/Delete an App Connector Group. +# +# Description +# =========== +# +# Quick examples of how to use the 'zpa_app_connector_groups' module to create an App Connector Group in the ZPA Cloud. +# +# This playbook requires proper API credentials to be passed statically or via environment variables connection +# to the ZPA Cloud 'zpa_client_id', 'zpa_client_secret' and zpa_customer_id. These may be defined as host variables +# (see `host_vars/zpa_cloud.yml` for an example) or extra vars. +# +# Modules Used +# ============ +# +# zpa_app_connector_groups - https://willguibr.github.io/zpacloud-ansible/modules/zpa_app_connector_groups.html +# +# Usage +# ===== +# +# $ ansible-playbook zpa_app_connector_groups.yml + +- hosts: localhost + connection: local + + vars: + app_connector_group: + - name: Example + description: Example + enabled: true + city_country: San Jose, US + country_code: US + latitude: 37.3382082 + longitude: -121.8863286 + location: San Jose, CA, USA + upgrade_day: SUNDAY + upgrade_time_in_secs: "66600" + override_version_profile: true + version_profile_name: Default + dns_query_type: IPV4_IPV6 + tcp_quick_ack_app: true + tcp_quick_ack_assistant: true + tcp_quick_ack_read_assistant: true + + + tasks: + + - name: Create/Update/Delete App Connector Group + zscaler.zpacloud.zpa_app_connector_groups: + name: "{{ item.name }}" + description: "{{ item.description }}" + enabled: "{{ item.enabled }}" + city_country: "{{ item.city_country }}" + country_code: "{{ item.country_code }}" + latitude: "{{ item.latitude }}" + longitude: "{{ item.longitude }}" + location: "{{ item.location }}" + upgrade_day: "{{ item.upgrade_day }}" + upgrade_time_in_secs: "{{ item.upgrade_time_in_secs }}" + override_version_profile: "{{ item.override_version_profile }}" + version_profile_name: "{{ item.version_profile_name }}" + dns_query_type: "{{ item.dns_query_type }}" + tcp_quick_ack_app: "{{ item.tcp_quick_ack_app }}" + tcp_quick_ack_assistant: "{{ item.tcp_quick_ack_assistant }}" + tcp_quick_ack_read_assistant: "{{ item.tcp_quick_ack_read_assistant }}" + state: absent + with_items: "{{ app_connector_group }}" + tags: app_connector_group + diff --git a/galaxy.yml b/galaxy.yml new file mode 100644 index 0000000..338cf4b --- /dev/null +++ b/galaxy.yml @@ -0,0 +1,62 @@ +### REQUIRED + +# The namespace of the collection. This can be a company/brand/organization or product namespace under which all +# content lives. May only contain alphanumeric characters and underscores. Additionally namespaces cannot start with +# underscores or numbers and cannot contain consecutive underscores +namespace: 'zscaler' + +# The name of the collection. Has the same character restrictions as 'namespace' +name: 'zpacloud' + +# The version of the collection. Must be compatible with semantic versioning +version: 1.1.1 + +# The path to the Markdown (.md) readme file. This path is relative to the root of the collection +readme: 'README.md' + +# A list of the collection's content authors. Can be just the name or in the format 'Full Name (url) +# @nicks:irc/im.site#channel' +authors: + - 'William Guilherme' + + +### OPTIONAL but strongly recommended + +# A short summary description of the collection +description: 'Collection for Zscaler Private Access (ZPA)' + +# Either a single license or a list of licenses for content inside of a collection. Ansible Galaxy currently only +# accepts L(SPDX,https://spdx.org/licenses/) licenses. This key is mutually exclusive with 'license_file' +#license: +#- MIT + +# The path to the license file for the collection. This path is relative to the root of the collection. This key is +# mutually exclusive with 'license' +license_file: 'LICENSE' + +# A list of tags you want to associate with the collection for indexing/searching. A tag name has the same character +# requirements as 'namespace' and 'name' +tags: + - zpa + - zpacloud + - zscaler + - zero_trust + - security + +# Collections that this collection requires to be installed for it to be usable. The key of the dict is the +# collection label 'namespace.name'. The value is a version range +# L(specifiers,https://python-semanticversion.readthedocs.io/en/latest/#requirement-specification). Multiple version +# range specifiers can be set and are separated by ',' +dependencies: {} + +# The URL of the originating SCM repository +repository: 'https://github.com/zscaler/zpacloud-ansible' + +# The URL to any online docs +documentation: 'https://zscaler.github.io/zpacloud-ansible/' + +# The URL to the homepage of the collection/project +homepage: 'https://github.com/zscaler/zpacloud-ansible' + +# The URL to the collection issue tracker +issues: 'https://github.com/zscaler/zpacloud-ansible/issues' diff --git a/meta/runtime.yml b/meta/runtime.yml new file mode 100644 index 0000000..1f18fd7 --- /dev/null +++ b/meta/runtime.yml @@ -0,0 +1,2 @@ +--- +requires_ansible: '>=2.9.10' \ No newline at end of file diff --git a/plugins/doc_fragments/fragments.py b/plugins/doc_fragments/fragments.py new file mode 100644 index 0000000..5eacc75 --- /dev/null +++ b/plugins/doc_fragments/fragments.py @@ -0,0 +1,35 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class ModuleDocFragment(object): + # Standard files documentation fragment + DOCUMENTATION = """ + """ + + STATE = """ +options: + state: + description: + - The state. + type: str + default: present + choices: + - present + - absent +""" + + ENABLED_STATE = """ +options: + state: + description: + - The state. + type: str + default: present + choices: + - present + - absent + - enabled + - disabled +""" diff --git a/plugins/module_utils/zpa_client.py b/plugins/module_utils/zpa_client.py new file mode 100644 index 0000000..5c6096f --- /dev/null +++ b/plugins/module_utils/zpa_client.py @@ -0,0 +1,83 @@ +# This code is part of Ansible, but is an independent component. +# This particular file snippet, and this file snippet only, is BSD licensed. +# Modules you write using this snippet, which is embedded dynamically by Ansible +# still belong to the author of the module, and may assign their own license +# to the complete work. +# +# Copyright (c) 2023 Zscaler Technology Alliances, +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible.module_utils.basic import env_fallback +from zscaler import ZPA + + +def deleteNone(_dict): + """Delete None values recursively from all of the dictionaries, tuples, lists, sets""" + if isinstance(_dict, dict): + for key, value in list(_dict.items()): + if isinstance(value, (list, dict, tuple, set)): + _dict[key] = deleteNone(value) + elif value is None or key is None: + del _dict[key] + elif isinstance(_dict, (list, set, tuple)): + _dict = type(_dict)(deleteNone(item) for item in _dict if item is not None) + return _dict + + +class ZPAClientHelper(ZPA): + def __init__(self, module): + ZPA.__init__( + self, + client_id=module.params.get("client_id", ""), + client_secret=module.params.get("client_secret", ""), + customer_id=module.params.get("customer_id", ""), + cloud=module.params.get("cloud", ""), + ) + + @staticmethod + def zpa_argument_spec(): + return dict( + client_id=dict( + no_log=True, + fallback=( + env_fallback, + ["ZPA_CLIENT_ID"], + ), + ), + client_secret=dict( + no_log=True, + fallback=( + env_fallback, + ["ZPA_CLIENT_SECRET"], + ), + ), + customer_id=dict( + no_log=True, + fallback=( + env_fallback, + ["ZPA_CUSTOMER_ID"], + ), + ), + ) diff --git a/plugins/modules/zpa_app_connector_controller_info.py b/plugins/modules/zpa_app_connector_controller_info.py new file mode 100644 index 0000000..869c2c9 --- /dev/null +++ b/plugins/modules/zpa_app_connector_controller_info.py @@ -0,0 +1,114 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, Zscaler Technology Alliances +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_app_connector_controller_info +short_description: Retrieves an app connector controller information +description: + - This module will allow the retrieval of information about an app connector controller. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - Name of the App Connector Group. + required: false + type: str + id: + description: + - ID of the App Connector Group. + required: false + type: str +""" + +EXAMPLES = """ +- name: Retrieve Details of all App Connector Groups + zscaler.zpacloud.zpa_app_connector_controller_info: + +- name: Retrieve Details of a Specific App Connector Groups by Name + zscaler.zpacloud.zpa_app_connector_controller_info: + name: "Example App Connector Group" + +- name: Retrieve Details of a Specific App Connector Groups by ID + zscaler.zpacloud.zpa_app_connector_controller_info: + id: "216196257331292046" +""" + +RETURN = """ +# Returns information on a specified App Connector Group. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, +) + + +def core(module: AnsibleModule): + connector_id = module.params.get("id", None) + connector_name = module.params.get("name", None) + client = ZPAClientHelper(module) + connectors = [] + if connector_id is not None: + connector_box = client.connectors.get_connector(connector_id=connector_id) + if connector_box is None: + module.fail_json( + msg="Failed to retrieve App Connector ID: '%s'" % (connector_id) + ) + connectors = [connector_box.to_dict()] + else: + connectors = client.connectors.list_connectors().to_list() + if connector_name is not None: + connector_found = False + for connector in connectors: + if connector.get("name") == connector_name: + connector_found = True + connectors = [connector] + if not connector_found: + module.fail_json( + msg="Failed to retrieve App Connector Name: '%s'" % (connector_name) + ) + module.exit_json(changed=False, data=connectors) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + name=dict(type="str", required=False), + id=dict(type="str", required=False), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_app_connector_groups.py b/plugins/modules/zpa_app_connector_groups.py new file mode 100644 index 0000000..690c287 --- /dev/null +++ b/plugins/modules/zpa_app_connector_groups.py @@ -0,0 +1,402 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_app_connector_groups +short_description: Create an App Connector Group in the ZPA Cloud. +description: + - This module creates/update/delete an App Connector Group in the ZPA Cloud. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - Name of the App Connector Group. + required: true + type: str + description: + description: "" + required: false + type: str + city_country: + description: + - City Country of the App Connector Group. + type: str + country_code: + description: + - Country code of the App Connector Group. + type: str + dns_query_type: + description: + - Whether to enable IPv4 or IPv6, or both, for DNS resolution of all applications in the App Connector Group. + type: str + choices: + - IPV4_IPV6 + - IPV4 + - IPV6 + default: IPV4_IPV6 + enabled: + description: + - Whether this App Connector Group is enabled or not. + type: bool + default: true + pra_enabled: + description: + - Whether or not privileged remote access is enabled for the App Connector Group. + type: bool + default: false + waf_disabled: + description: + - Whether or not AppProtection is disabled for the App Connector Group. + type: bool + default: false + latitude: + description: + - Latitude of the App Connector Group. Integer or decimal. With values in the range of -90 to 90. + required: false + type: str + location: + description: + - Location of the App Connector Group. + required: false + type: str + longitude: + description: + - Longitude of the App Connector Group. Integer or decimal. With values in the range of -180 to 180. + required: false + type: str + lss_app_connector_group: + description: + - LSS app connector group + required: false + type: str + upgrade_day: + description: + - App Connectors in this group will attempt to update to a newer version of the software during this specified day. + - List of valid days (i.e., Sunday, Monday). + default: SUNDAY + type: str + upgrade_time_in_secs: + description: + - App Connectors in this group will attempt to update to a newer version of the software during this specified time. + - Integer in seconds (i.e., -66600). The integer should be greater than or equal to 0 and less than 86400, in 15 minute intervals. + default: 66600 + type: str + override_version_profile: + description: + - App Connectors in this group will attempt to update to a newer version of the software during this specified time. + - Integer in seconds (i.e., -66600). The integer should be greater than or equal to 0 and less than 86400, in 15 minute intervals. + type: bool + default: false + version_profile_id: + description: + - ID of the version profile. To learn more, see Version Profile Use Cases. + - This value is required, if the value for overrideVersionProfile is set to true. + type: str + default: '0' + choices: + - 0 + - 1 + - 2 + version_profile_name: + description: + - Name of the version profile. + type: str + state: + description: + - Whether the app connector group should be present or absent. + type: str + choices: + - present + - absent + default: present +""" + +EXAMPLES = """ +- name: Create/Update/Delete an App Connector Group + zscaler.zpacloud.zpa_app_connector_groups: + name: "Example" + description: "Example2" + enabled: true + city_country: "California, US" + country_code: "US" + latitude: "37.3382082" + longitude: "-121.8863286" + location: "San Jose, CA, USA" + upgrade_day: "SUNDAY" + upgrade_time_in_secs: "66600" + override_version_profile: true + version_profile_id: "0" + dns_query_type: "IPV4" +""" + +RETURN = """ +# The newly created app connector group resource record. +""" + + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + deleteNone, + ZPAClientHelper, +) + +def validate_latitude(val): + try: + v = float(val) + if v < -90 or v > 90: + return (None, ["latitude must be between -90 and 90"]) + except ValueError: + return (None, ["latitude value should be a valid float number"]) + return (None, None) + +def validate_longitude(val): + try: + v = float(val) + if v < -180 or v > 180: + return (None, ["longitude must be between -180 and 180"]) + except ValueError: + return (None, ["longitude value should be a valid float number"]) + return (None, None) + +def diff_suppress_func_coordinate(old, new): + try: + o = round(float(old) * 1000000) / 1000000 + n = round(float(new) * 1000000) / 1000000 + return o == n + except ValueError: + return False + +def validate_tcp_quick_ack(tcp_quick_ack_app, tcp_quick_ack_assistant, tcp_quick_ack_read_assistant): + if tcp_quick_ack_app != tcp_quick_ack_assistant or \ + tcp_quick_ack_app != tcp_quick_ack_read_assistant or \ + tcp_quick_ack_assistant != tcp_quick_ack_read_assistant: + return "the values of tcpQuickAck related flags need to be consistent" + return None + +def core(module): + state = module.params.get("state", None) + client = ZPAClientHelper(module) + group = dict() + + latitude = module.params.get('latitude') + longitude = module.params.get('longitude') + _, lat_errors = validate_latitude(latitude) + _, lon_errors = validate_longitude(longitude) + + if lat_errors or lon_errors: + all_errors = lat_errors + lon_errors + module.fail_json(msg=", ".join(all_errors)) + + # Validate the TCP Quick Ack attributes + tcp_quick_ack_app = module.params['tcp_quick_ack_app'] + tcp_quick_ack_assistant = module.params['tcp_quick_ack_assistant'] + tcp_quick_ack_read_assistant = module.params['tcp_quick_ack_read_assistant'] + + tcp_quick_ack_error = validate_tcp_quick_ack(tcp_quick_ack_app, tcp_quick_ack_assistant, tcp_quick_ack_read_assistant) + if tcp_quick_ack_error: + module.fail_json(msg=tcp_quick_ack_error) + + group = dict() + params = [ + "id", + "name", + "description", + "enabled", + "city_country", + "country_code", + "latitude", + "longitude", + "location", + "upgrade_day", + "upgrade_time_in_secs", + "override_version_profile", + "version_profile_id", + "version_profile_name", + "dns_query_type", + "lss_app_connector_group", + "tcp_quick_ack_app", + "tcp_quick_ack_assistant", + "tcp_quick_ack_read_assistant", + "use_in_dr_mode", + "pra_enabled", + "waf_disabled", + ] + for param_name in params: + group[param_name] = module.params.get(param_name, None) + group_id = group.get("id", None) + group_name = group.get("name", None) + existing_group = None + if group_id is not None: + group_box = client.connector_groups.get_group(group_id=group_id) + if group_box is not None: + existing_group = group_box.to_dict() + elif group_name is not None: + groups = client.connector_groups.list_groups().to_list() + for group_ in groups: + if group_.get("name") == group_name: + existing_group = group_ + if existing_group is not None: + id = existing_group.get("id") + existing_group.update(group) + existing_group["id"] = id + if state == "present": + if existing_group is not None: + """Update""" + # Check if latitude and longitude need to be updated + existing_lat = existing_group.get("latitude") + new_lat = group.get("latitude") + if diff_suppress_func_coordinate(existing_lat, new_lat): + existing_group["latitude"] = existing_lat # reset to original if they're deemed equal + + existing_long = existing_group.get("longitude") + new_long = group.get("longitude") + if diff_suppress_func_coordinate(existing_long, new_long): + existing_group["longitude"] = existing_long # reset to original if they're deemed equal + + existing_group = deleteNone( + dict( + group_id=existing_group.get("id"), + name=existing_group.get("name"), + description=existing_group.get("description"), + enabled=existing_group.get("enabled"), + city_country=existing_group.get("city_country"), + country_code=existing_group.get("country_code"), + latitude=existing_group.get("latitude"), + longitude=existing_group.get("longitude"), + location=existing_group.get("location"), + upgrade_day=existing_group.get("upgrade_day"), + upgrade_time_in_secs=existing_group.get("upgrade_time_in_secs"), + override_version_profile=existing_group.get("override_version_profile"), + version_profile_id=existing_group.get("version_profile_id"), + version_profile_name=existing_group.get("version_profile_name"), + dns_query_type=existing_group.get("dns_query_type"), + tcp_quick_ack_app=existing_group.get("tcp_quick_ack_app"), + tcp_quick_ack_assistant=existing_group.get("tcp_quick_ack_assistant"), + tcp_quick_ack_read_assistant=existing_group.get("tcp_quick_ack_read_assistant"), + use_in_dr_mode=existing_group.get("use_in_dr_mode"), + pra_enabled=existing_group.get("pra_enabled"), + waf_disabled=existing_group.get("waf_disabled"), + ) + ) + existing_group = client.connectors.update_connector_group( + **existing_group + ).to_dict() + module.exit_json(changed=True, data=existing_group) + else: + """Create""" + group = deleteNone( + dict( + name=group.get("name"), + description=group.get("description"), + enabled=group.get("enabled"), + city_country=group.get("city_country"), + country_code=group.get("country_code"), + latitude=group.get("latitude"), + longitude=group.get("longitude"), + location=group.get("location"), + upgrade_day=group.get("upgrade_day"), + upgrade_time_in_secs=group.get("upgrade_time_in_secs"), + override_version_profile=group.get("override_version_profile"), + version_profile_id=group.get("version_profile_id"), + version_profile_name=group.get("version_profile_name"), + dns_query_type=group.get("dns_query_type"), + tcp_quick_ack_app=group.get("tcp_quick_ack_app"), + tcp_quick_ack_assistant=group.get("tcp_quick_ack_assistant"), + tcp_quick_ack_read_assistant=group.get("tcp_quick_ack_read_assistant"), + use_in_dr_mode=group.get("use_in_dr_mode"), + pra_enabled=group.get("pra_enabled"), + waf_disabled=group.get("waf_disabled"), + ) + ) + group = client.connectors.add_connector_group(**group).to_dict() + module.exit_json(changed=True, data=group) + elif state == "absent": + if existing_group is not None and existing_group.get("id") is not None: + code = client.connectors.delete_connector_group( + group_id=existing_group.get("id") + ) + if code > 299: + module.exit_json(changed=False, data=None) + module.exit_json(changed=True, data=existing_group) + module.exit_json(changed=False, data={}) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + id_name_spec = dict( + type="list", + elements="dict", + options=dict( + id=dict(type="str", required=False), name=dict(type="str", required=False) + ), + required=False, + ) + argument_spec.update( + connectors=id_name_spec, + name=dict(type="str", required=True), + id=dict(type="str", required=False), + city_country=dict(type="str", required=False), + country_code=dict(type="str", required=False), + description=dict(type="str", required=False), + dns_query_type=dict( + type="str", + choices=["IPV4_IPV6", "IPV4", "IPV6"], + required=False, + default="IPV4_IPV6", + ), + enabled=dict(type="bool", default=True, required=False), + latitude=dict(type="str", required=False), + location=dict(type="str", required=False), + longitude=dict(type="str", required=False), + lss_app_connector_group=dict(type="str", required=False), + upgrade_day=dict(type="str", default="SUNDAY", choices=["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY"], required=False), + upgrade_time_in_secs=dict(type="str", default=66600, required=False), + override_version_profile=dict(type="bool", default=False, required=False), + version_profile_id=dict( + type="str", default="0", choices=["0", "1", "2"], required=False + ), + version_profile_name=dict(type="str", choices=["Default", "Previous Default", "New Release"], required=False), + tcp_quick_ack_app=dict(type="bool", default=False, required=False), + tcp_quick_ack_assistant=dict(type="bool", default=False, required=False), + tcp_quick_ack_read_assistant=dict(type="bool", default=False, required=False), + use_in_dr_mode=dict(type="bool", default=False, required=False), + pra_enabled=dict(type="bool", default=False, required=False), + waf_disabled=dict(type="bool", default=False, required=False), + state=dict(type="str", choices=["present", "absent"], default="present"), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_app_connector_groups_info.py b/plugins/modules/zpa_app_connector_groups_info.py new file mode 100644 index 0000000..1534a57 --- /dev/null +++ b/plugins/modules/zpa_app_connector_groups_info.py @@ -0,0 +1,115 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_app_connector_groups_info +short_description: Retrieves an app connector group information +description: + - This module will allow the retrieval of information about an app connector group. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - Name of the App Connector Group. + required: false + type: str + id: + description: + - ID of the App Connector Group. + required: false + type: str +""" + +EXAMPLES = """ +- name: Retrieve Details of all App Connector Groups + zscaler.zpacloud.zpa_app_connector_groups_info: + +- name: Retrieve Details of a Specific App Connector Groups by Name + zscaler.zpacloud.zpa_app_connector_groups_info: + name: "Example App Connector Group" + +- name: Retrieve Details of a Specific App Connector Groups by ID + zscaler.zpacloud.zpa_app_connector_groups_info: + id: "216196257331292046" +""" + +RETURN = """ +# Returns information on a specified App Connector Group. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, +) + + +def core(module: AnsibleModule): + group_id = module.params.get("id", None) + group_name = module.params.get("name", None) + client = ZPAClientHelper(module) + groups = [] + if group_id is not None: + group_box = client.connector_groups.get_group(group_id=group_id) + if group_box is None: + module.fail_json( + msg="Failed to retrieve App Connector Group ID: '%s'" % (group_id) + ) + groups = [group_box.to_dict()] + else: + groups = client.connector_groups.list_groups().to_list() + if group_name is not None: + group_found = False + for group in groups: + if group.get("name") == group_name: + group_found = True + groups = [group] + if not group_found: + module.fail_json( + msg="Failed to retrieve App Connector Group Name: '%s'" + % (group_name) + ) + module.exit_json(changed=False, data=groups) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + name=dict(type="str", required=False), + id=dict(type="str", required=False), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_application_segment.py b/plugins/modules/zpa_application_segment.py new file mode 100644 index 0000000..58d8a72 --- /dev/null +++ b/plugins/modules/zpa_application_segment.py @@ -0,0 +1,428 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_application_segment +short_description: Create an application segment in the ZPA Cloud. +description: + - This module will create/update/delete an application segment +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - Name of the application. + required: true + type: str + id: + description: + - ID of the application. + required: false + type: str + description: + description: + - Description of the application. + required: false + type: str + default_max_age: + description: + - default_max_age + required: false + type: str + ip_anchored: + description: + - Whether Source IP Anchoring for use with ZIA, is enabled or disabled for the app. + type: bool + required: false + tcp_port_range: + type: list + elements: dict + description: + - List of tcp port range pairs, e.g. [22, 22] for port 22-22, [80, 100] for 80-100. + required: false + suboptions: + from: + type: str + required: false + description: + - List of valid TCP ports. The application segment API supports multiple TCP and UDP port ranges. + to: + type: str + required: false + description: + - List of valid TCP ports. The application segment API supports multiple TCP and UDP port ranges. + udp_port_range: + type: list + elements: dict + description: + - List of udp port range pairs, e.g. ['35000', '35000'] for port 35000. + required: false + suboptions: + from: + type: str + required: false + description: + - List of valid UDP ports. The application segment API supports multiple TCP and UDP port ranges. + to: + type: str + required: false + description: + - List of valid UDP ports. The application segment API supports multiple TCP and UDP port ranges. + double_encrypt: + description: + - Whether Double Encryption is enabled or disabled for the app. + type: bool + required: false + icmp_access_type: + description: + - icmp access type. + type: str + required: false + choices: + - PING_TRACEROUTING + - PING + - NONE + default: NONE + default_idle_timeout: + description: + - default idle timeout. + type: str + required: false + passive_health_enabled: + description: + - passive health enabled. + type: bool + required: false + bypass_type: + description: + - Indicates whether users can bypass ZPA to access applications. + type: str + required: false + choices: + - ALWAYS + - NEVER + - ON_NET + default: NEVER + is_cname_enabled: + description: + - Indicates if the Zscaler Client Connector (formerly Zscaler App or Z App) receives CNAME DNS records from the connectors. + type: bool + required: false + config_space: + description: + - config space. + type: str + required: false + choices: + - DEFAULT + - SIEM + default: DEFAULT + health_reporting: + description: + - Whether health reporting for the app is Continuous or On Access. Supported values are NONE, ON_ACCESS, CONTINUOUS + type: str + required: false + choices: + - NONE + - ON_ACCESS + - CONTINUOUS + default: NONE + server_group_ids: + description: + - ID of the server group. + type: list + elements: dict + required: true + suboptions: + name: + required: false + type: str + description: "" + id: + required: true + type: str + description: "" + segment_group_id: + description: + - ID of the segment group. + type: str + required: true + segment_group_name: + description: + - segment group name. + type: str + required: false + health_check_type: + description: + - health check type. + type: str + required: false + enabled: + description: + - Whether this application is enabled or not. + type: bool + required: false + domain_names: + description: + - List of domains and IPs. + type: list + elements: str + required: true + state: + description: "Whether the app should be present or absent." + type: str + choices: + - present + - absent + default: present +""" + +EXAMPLES = """ +- name: Create/Update/Delete an application segment. + zscaler.zpacloud.zpa_application_segment: + name: Example Application Segment + description: Example Application Segment + enabled: true + health_reporting: ON_ACCESS + bypass_type: NEVER + is_cname_enabled: true + tcp_port_range: + - from: "80" + to: "80" + domain_names: + - crm.example.com + segment_group_id: "216196257331291896" + server_group_ids: + - "216196257331291969" +""" + +RETURN = """ +# The newly created application segment resource record. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, + deleteNone, +) + + +def convert_ports_list(obj_list): + if obj_list is None: + return [] + r = [] + for o in obj_list: + if o.get("from", None) is not None and o.get("to", None) is not None: + r.append("" + o.get("from")) + r.append("" + o.get("to")) + return r + + +def convert_ports(obj_list): + if obj_list is None: + return [] + r = [] + for o in obj_list: + if o.get("from", None) is not None and o.get("to", None) is not None: + c = (o.get("from"), o.get("to")) + r.append(c) + return r + + +def core(module): + state = module.params.get("state", None) + client = ZPAClientHelper(module) + app = dict() + params = [ + "tcp_port_range", + "enabled", + "default_idle_timeout", + "bypass_type", + "udp_port_range", + "config_space", + "health_reporting", + "segment_group_id", + "double_encrypt", + "health_check_type", + "default_max_age", + "is_cname_enabled", + "passive_health_enabled", + "ip_anchored", + "name", + "description", + "icmp_access_type", + "id", + "server_group_ids", + "segment_group_name", + "domain_names", + ] + for param_name in params: + app[param_name] = module.params.get(param_name) + appsegment_id = module.params.get("id", None) + appsegment_name = module.params.get("name", None) + existing_app = None + if appsegment_id is not None: + existing_app = client.app_segments.get_segment(segment_id=appsegment_id) + elif appsegment_name is not None: + ba_app_segments = client.app_segments.list_segments().to_list() + for ba_app_segment in ba_app_segments: + if ba_app_segment.get("name") == appsegment_name: + existing_app = ba_app_segment + break + if existing_app is not None: + id = existing_app.get("id") + existing_app.update(app) + existing_app["id"] = id + if state == "present": + if existing_app is not None: + """Update""" + existing_app = deleteNone( + dict( + segment_id=existing_app.get("id"), + bypass_type=existing_app.get("bypass_type", None), + clientless_app_ids=existing_app.get("clientless_apps", None), + config_space=existing_app.get("config_space", None), + default_idle_timeout=existing_app.get("default_idle_timeout", None), + default_max_age=existing_app.get("default_max_age", None), + description=existing_app.get("description", None), + domain_names=existing_app.get("domain_names", None), + double_encrypt=existing_app.get("double_encrypt", None), + enabled=existing_app.get("enabled", None), + health_check_type=existing_app.get("health_check_type", None), + health_reporting=existing_app.get("health_reporting", None), + ip_anchored=existing_app.get("ip_anchored", None), + is_cname_enabled=existing_app.get("is_cname_enabled", None), + name=existing_app.get("name", None), + passive_health_enabled=existing_app.get( + "passive_health_enabled", None + ), + segment_group_id=existing_app.get("segment_group_id", None), + server_group_ids=existing_app.get("server_group_ids", None), + tcp_ports=convert_ports(existing_app.get("tcp_port_range", None)), + udp_ports=convert_ports(existing_app.get("udp_port_range", None)), + ) + ) + app = client.app_segments.update_segment(**existing_app) + module.exit_json(changed=True, data=app) + else: + """Create""" + app = deleteNone( + dict( + bypass_type=app.get("bypass_type", None), + clientless_app_ids=app.get("clientless_apps", None), + config_space=app.get("config_space", None), + default_idle_timeout=app.get("default_idle_timeout", None), + default_max_age=app.get("default_max_age", None), + description=app.get("description", None), + domain_names=app.get("domain_names", None), + double_encrypt=app.get("double_encrypt", None), + enabled=app.get("enabled", None), + health_check_type=app.get("health_check_type", None), + health_reporting=app.get("health_reporting", None), + ip_anchored=app.get("ip_anchored", None), + is_cname_enabled=app.get("is_cname_enabled", None), + name=app.get("name", None), + passive_health_enabled=app.get("passive_health_enabled", None), + segment_group_id=app.get("segment_group_id", None), + server_group_ids=app.get("server_group_ids", None), + tcp_ports=convert_ports_list(app.get("tcp_port_range", None)), + udp_ports=convert_ports_list(app.get("udp_port_range", None)), + ) + ) + app = client.app_segments.add_segment(**app) + module.exit_json(changed=False, data=app) + elif state == "absent" and existing_app is not None: + client.app_segments.delete_segment(existing_app.get("id"), force_delete=True) + module.exit_json(changed=True, data=existing_app) + module.exit_json(changed=False, data={}) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + port_spec = dict(to=dict(type="str", required=False)) + port_spec["from"] = dict(type="str", required=False) + id_name_spec = dict( + type="list", + elements="str", + required=True, + ) + argument_spec.update( + tcp_port_range=dict( + type="list", elements="dict", options=port_spec, required=False + ), + enabled=dict(type="bool", required=False), + default_idle_timeout=dict(type="str", required=False, default=""), + bypass_type=dict( + type="str", + required=False, + default="NEVER", + choices=["ALWAYS", "NEVER", "ON_NET"], + ), + udp_port_range=dict( + type="list", elements="dict", options=port_spec, required=False + ), + config_space=dict( + type="str", required=False, default="DEFAULT", choices=["DEFAULT", "SIEM"] + ), + health_reporting=dict( + type="str", + required=False, + default="NONE", + choices=["NONE", "ON_ACCESS", "CONTINUOUS"], + ), + segment_group_id=dict(type="str", required=True), + double_encrypt=dict(type="bool", required=False), + health_check_type=dict(type="str"), + default_max_age=dict(type="str", required=False, default=""), + is_cname_enabled=dict(type="bool", required=False), + passive_health_enabled=dict(type="bool", required=False), + ip_anchored=dict(type="bool", required=False), + name=dict(type="str", required=True), + description=dict(type="str", required=False), + icmp_access_type=dict( + type="str", + required=False, + default="NONE", + choices=["PING_TRACEROUTING", "PING", "NONE"], + ), + id=dict(type="str", required=False), + server_group_ids=id_name_spec, + segment_group_name=dict(type="str", required=False), + domain_names=dict(type="list", elements="str", required=True), + state=dict(type="str", choices=["present", "absent"], default="present"), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_application_segment_browser_access.py b/plugins/modules/zpa_application_segment_browser_access.py new file mode 100644 index 0000000..0e9520b --- /dev/null +++ b/plugins/modules/zpa_application_segment_browser_access.py @@ -0,0 +1,485 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_application_segment_browser_access +short_description: Create a Browser Access Application Segment. +description: + - This module create/update/delete a Browser Access Application Segment in the ZPA Cloud. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + default_max_age: + type: str + required: False + default: "" + description: "default_max_age" + ip_anchored: + type: bool + required: False + description: "ip_anchored" + udp_port_range: + type: list + elements: dict + required: False + description: "udp port range" + suboptions: + to: + type: str + required: False + description: "" + from: + type: str + required: False + description: "" + id: + type: str + description: "Unique ID." + double_encrypt: + type: bool + required: False + description: "Whether Double Encryption is enabled or disabled for the app." + icmp_access_type: + type: str + required: False + default: "NONE" + choices: ["PING_TRACEROUTING", "PING", "NONE"] + description: "icmp access type." + default_idle_timeout: + type: str + required: False + default: "" + description: "default idle timeout." + passive_health_enabled: + type: bool + required: False + description: "passive health enabled." + bypass_type: + type: str + required: False + description: "Indicates whether users can bypass ZPA to access applications." + choices: ["ALWAYS", "NEVER", "ON_NET"] + is_cname_enabled: + type: bool + required: False + description: "Indicates if the Zscaler Client Connector (formerly Zscaler App or Z App) receives CNAME DNS records from the connectors." + name: + type: str + required: True + description: "Name of the application." + config_space: + type: str + required: False + default: "DEFAULT" + choices: ["DEFAULT", "SIEM"] + description: "config space." + health_reporting: + type: str + required: False + description: "Whether health reporting for the app is Continuous or On Access. Supported values: NONE, ON_ACCESS, CONTINUOUS." + default: "NONE" + choices: ["NONE", "ON_ACCESS", "CONTINUOUS"] + server_group_ids: + type: list + elements: str + required: True + description: "List of the server group IDs." + segment_group_id: + type: str + required: True + description: "segment group id." + description: + type: str + required: False + description: "Description of the application." + health_check_type: + type: str + description: "health check type." + segment_group_name: + type: str + required: False + description: "segment group name." + tcp_port_range: + type: list + elements: dict + required: False + description: "tcp port range" + suboptions: + to: + type: str + required: False + description: "" + from: + type: str + required: False + description: "" + enabled: + type: bool + required: False + description: "Whether this application is enabled or not." + domain_names: + type: list + elements: str + required: True + description: "List of domains and IPs." + clientless_apps: + description: "" + type: list + elements: dict + suboptions: + path: + type: str + required: False + description: "" + trust_untrusted_cert: + type: bool + required: False + description: "" + allow_options: + type: bool + required: False + description: "" + description: + type: str + required: False + description: "" + id: + type: str + description: "" + cname: + type: str + required: False + description: "" + hidden: + type: bool + required: False + description: "" + app_id: + type: str + description: "" + application_port: + type: str + required: False + description: "" + application_protocol: + type: str + required: True + description: "" + name: + type: str + required: True + description: "" + certificate_id: + type: str + required: True + description: "" + certificate_name: + type: str + required: False + description: "" + domain: + type: str + required: False + description: "" + enabled: + type: bool + required: False + description: "" + local_domain: + type: str + required: False + description: "" + required: False + state: + description: "Whether the app should be present or absent." + type: str + choices: + - present + - absent + default: present +""" + +EXAMPLES = """ +- name: Create an app segment + zscaler.zpacloud.zpa_browser_access: + name: Example Application + description: Example Application Test + enabled: true + health_reporting: ON_ACCESS + bypass_type: NEVER + clientless_apps: + - name: "crm.example.com" + application_protocol: "HTTP" + application_port: "8080" + certificate_id: "216196257331282583" + trust_untrusted_cert: true + enabled: true + domain: "crm.example.com" + - name: "crm2.example.com" + application_protocol: "HTTP" + application_port: "8082" + certificate_id: "216196257331282583" + trust_untrusted_cert: true + enabled: true + domain: "crm.example.com" + is_cname_enabled: true + tcp_port_range: + - from: "80" + to: "80" + domain_names: + - crm.example.com + segment_group_id: "216196257331291896" + server_group_ids: + - "216196257331291969" +""" + +RETURN = """ +# The newly created browser access application segment resource record. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, + deleteNone, +) + + +def convert_ports_list(obj_list): + if obj_list is None: + return [] + r = [] + for o in obj_list: + if o.get("from", None) is not None and o.get("to", None) is not None: + r.append("" + o.get("from")) + r.append("" + o.get("to")) + return r + + +def convert_ports(obj_list): + if obj_list is None: + return [] + r = [] + for o in obj_list: + if o.get("from", None) is not None and o.get("to", None) is not None: + c = (o.get("from"), o.get("to")) + r.append(c) + return r + + +def core(module): + state = module.params.get("state", None) + ba_appsegment_id = module.params.get("id", None) + ba_appsegment_name = module.params.get("name", None) + client = ZPAClientHelper(module) + app = dict() + params = [ + "segment_group_id", + "segment_group_name", + "bypass_type", + "clientless_apps", + "config_space", + "default_idle_timeout", + "default_max_age", + "description", + "domain_names", + "double_encrypt", + "enabled", + "health_check_type", + "health_reporting", + "icmp_access_type", + "id", + "ip_anchored", + "is_cname_enabled", + "name", + "passive_health_enabled", + "tcp_port_range", + "udp_port_range", + "server_group_ids", + ] + for param_name in params: + app[param_name] = module.params.get(param_name) + existing_app = None + if ba_appsegment_id is not None: + existing_app = client.app_segments.get_segment(segment_id=ba_appsegment_id) + elif ba_appsegment_name is not None: + ba_app_segments = client.app_segments.list_segments().to_list() + for ba_app_segment in ba_app_segments: + if ba_app_segment.get("name") == ba_appsegment_name: + existing_app = ba_app_segment + break + if existing_app is not None: + id = existing_app.get("id") + existing_app.update(app) + existing_app["id"] = id + if state == "present": + if existing_app is not None: + """Update""" + existing_app = deleteNone( + dict( + segment_id=existing_app.get("id"), + bypass_type=existing_app.get("bypass_type", None), + clientless_app_ids=existing_app.get("clientless_apps", None), + config_space=existing_app.get("config_space", None), + default_idle_timeout=existing_app.get("default_idle_timeout", None), + default_max_age=existing_app.get("default_max_age", None), + description=existing_app.get("description", None), + domain_names=existing_app.get("domain_names", None), + double_encrypt=existing_app.get("double_encrypt", None), + enabled=existing_app.get("enabled", None), + health_check_type=existing_app.get("health_check_type", None), + health_reporting=existing_app.get("health_reporting", None), + ip_anchored=existing_app.get("ip_anchored", None), + is_cname_enabled=existing_app.get("is_cname_enabled", None), + name=existing_app.get("name", None), + passive_health_enabled=existing_app.get( + "passive_health_enabled", None + ), + segment_group_id=existing_app.get("segment_group_id", None), + server_group_ids=existing_app.get("server_group_ids", None), + tcp_ports=convert_ports(existing_app.get("tcp_port_range", None)), + udp_ports=convert_ports(existing_app.get("udp_port_range", None)), + ) + ) + existing_app = client.app_segments.update_segment(**existing_app) + module.exit_json(changed=True, data=existing_app) + else: + """Create""" + app = deleteNone( + dict( + bypass_type=app.get("bypass_type", None), + clientless_app_ids=app.get("clientless_apps", None), + config_space=app.get("config_space", None), + default_idle_timeout=app.get("default_idle_timeout", None), + default_max_age=app.get("default_max_age", None), + description=app.get("description", None), + domain_names=app.get("domain_names", None), + double_encrypt=app.get("double_encrypt", None), + enabled=app.get("enabled", None), + health_check_type=app.get("health_check_type", None), + health_reporting=app.get("health_reporting", None), + ip_anchored=app.get("ip_anchored", None), + is_cname_enabled=app.get("is_cname_enabled", None), + name=app.get("name", None), + passive_health_enabled=app.get("passive_health_enabled", None), + segment_group_id=app.get("segment_group_id", None), + server_group_ids=app.get("server_group_ids", None), + tcp_ports=convert_ports_list(app.get("tcp_port_range", None)), + udp_ports=convert_ports_list(app.get("udp_port_range", None)), + ) + ) + app = client.app_segments.add_segment(**app) + module.exit_json(changed=False, data=app) + elif state == "absent" and existing_app is not None: + code = client.app_segments.delete_segment( + segment_id=existing_app.get("id"), force_delete=True + ) + if code > 299: + module.exit_json(changed=False, data=None) + module.exit_json(changed=True, data=existing_app) + module.exit_json(changed=False, data={}) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + port_spec = dict(to=dict(type="str", required=False)) + port_spec["from"] = dict(type="str", required=False) + argument_spec.update( + tcp_port_range=dict( + type="list", elements="dict", options=port_spec, required=False + ), + enabled=dict(type="bool", required=False), + default_idle_timeout=dict(type="str", required=False, default=""), + bypass_type=dict( + type="str", required=False, choices=["ALWAYS", "NEVER", "ON_NET"] + ), + udp_port_range=dict( + type="list", elements="dict", options=port_spec, required=False + ), + config_space=dict( + type="str", required=False, default="DEFAULT", choices=["DEFAULT", "SIEM"] + ), + health_reporting=dict( + type="str", + required=False, + default="NONE", + choices=["NONE", "ON_ACCESS", "CONTINUOUS"], + ), + segment_group_id=dict(type="str", required=True), + double_encrypt=dict(type="bool", required=False), + health_check_type=dict(type="str"), + default_max_age=dict(type="str", required=False, default=""), + is_cname_enabled=dict(type="bool", required=False), + passive_health_enabled=dict(type="bool", required=False), + ip_anchored=dict(type="bool", required=False), + name=dict(type="str", required=True), + description=dict(type="str", required=False), + icmp_access_type=dict( + type="str", + required=False, + default="NONE", + choices=["PING_TRACEROUTING", "PING", "NONE"], + ), + id=dict(type="str"), + server_group_ids=dict(type="list", elements="str", required=True), + segment_group_name=dict(type="str", required=False), + domain_names=dict(type="list", elements="str", required=True), + clientless_apps=dict( + type="list", + elements="dict", + options=dict( + path=dict(type="str", required=False), + trust_untrusted_cert=dict(type="bool", required=False), + allow_options=dict(type="bool", required=False), + description=dict(type="str", required=False), + id=dict(type="str"), + cname=dict(type="str", required=False), + hidden=dict(type="bool", required=False), + app_id=dict(type="str"), + application_port=dict(type="str", required=False), + application_protocol=dict(type="str", required=True), + name=dict(type="str", required=True), + certificate_id=dict(type="str", required=True), + certificate_name=dict(type="str", required=False), + domain=dict(type="str", required=False), + enabled=dict(type="bool", required=False), + local_domain=dict(type="str", required=False), + ), + required=False, + ), + state=dict(type="str", choices=["present", "absent"], default="present"), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_application_segment_browser_access_info.py b/plugins/modules/zpa_application_segment_browser_access_info.py new file mode 100644 index 0000000..363a117 --- /dev/null +++ b/plugins/modules/zpa_application_segment_browser_access_info.py @@ -0,0 +1,123 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_application_segment_browser_access_info +short_description: Retrieves browser access application segment information. +description: + - This module will allow the retrieval of information about a browser access application segment. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - Name of the App Connector Group. + required: false + type: str + id: + description: + - ID of the App Connector Group. + required: false + type: str +""" + +EXAMPLES = """ +- name: Gather information about all browser access application segments + zscaler.zpacloud.zpa_browser_access_info: + register: all_browser_access + +- debug: + msg: "{{ all_browser_access }}" + +- name: Browser Access Application Segment by Name + zscaler.zpacloud.zpa_browser_access_info: + name: "Example" +- name: Browser Access Application Segment by ID + zscaler.zpacloud.zpa_browser_access_info: + id: "198288282" + +""" + +RETURN = """ +# Returns information on a specified Browser Access Application Segment. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, +) + + +def core(module: AnsibleModule): + ba_appsegment_id = module.params.get("id", None) + ba_appsegment_name = module.params.get("name", None) + client = ZPAClientHelper(module) + ba_app_segments = [] + if ba_appsegment_id is not None: + ba_app_segment_box = client.app_segments.get_segment( + segment_id=ba_appsegment_id + ) + if ba_app_segment_box is None: + module.fail_json( + msg="Failed to retrieve Browser Access Application Segment ID: '%s'" + % (ba_appsegment_id) + ) + ba_app_segments = [ba_app_segment_box.to_dict()] + else: + ba_app_segments = client.app_segments.list_segments().to_list() + if ba_appsegment_name is not None: + ba_app_segment_found = False + for ba_app_segment in ba_app_segments: + if ba_app_segment.get("name") == ba_appsegment_name: + ba_app_segment_found = True + ba_app_segments = [ba_app_segment] + break + if not ba_app_segment_found: + module.fail_json( + msg="Failed to retrieve Browser Access Certificate Name: '%s'" + % (ba_appsegment_name) + ) + module.exit_json(changed=False, data=ba_app_segments) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + name=dict(type="str", required=False), + id=dict(type="str", required=False), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_application_segment_info.py b/plugins/modules/zpa_application_segment_info.py new file mode 100644 index 0000000..97f3b9c --- /dev/null +++ b/plugins/modules/zpa_application_segment_info.py @@ -0,0 +1,113 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_application_segment_info +short_description: Retrieve an application segment information. +description: + - This module will allow the retrieval of information about an application segment. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: "Name of the application segment." + required: false + type: str + id: + description: "ID of the application segment." + required: False + type: str +""" + +EXAMPLES = """ +- name: Retrieve Details of All Application Segments + zscaler.zpacloud.zpa_application_segment_info: + +- name: Retrieve Details of a Specific Application Segments by Name + zscaler.zpacloud.zpa_application_segment_info: + name: "Example Application Segment" + +- name: Retrieve Details of a Specific Application Segments by ID + zscaler.zpacloud.zpa_application_segment_info: + id: "216196257331291981" +""" + +RETURN = """ +# Returns information on a specified Application Segment. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, +) + + +def core(module: AnsibleModule): + segment_id = module.params.get("id", None) + segment_name = module.params.get("name", None) + client = ZPAClientHelper(module) + app_segments = [] + if segment_id is not None: + segment_box = client.app_segments.get_segment(segment_id=segment_id) + if segment_box is None: + module.fail_json( + msg="Failed to retrieve Application Segment ID: '%s'" % (segment_id) + ) + app_segments = [segment_box.to_dict()] + else: + app_segments = client.app_segments.list_segments().to_list() + if segment_name is not None: + app_segment_found = False + for app_segment in app_segments: + if app_segment.get("name") == segment_name: + app_segment_found = True + app_segments = [app_segment] + if not app_segment_found: + module.fail_json( + msg="Failed to retrieve Application Segment Name: '%s'" + % (segment_name) + ) + module.exit_json(changed=False, data=app_segments) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + name=dict(type="str", required=False), + id=dict(type="str", required=False), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_application_server.py b/plugins/modules/zpa_application_server.py new file mode 100644 index 0000000..9080671 --- /dev/null +++ b/plugins/modules/zpa_application_server.py @@ -0,0 +1,198 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_application_server +short_description: Create an application server in the ZPA Cloud. +description: + - This module creates/update/delete an application server in the ZPA Cloud. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - This field defines the name of the server to create. + required: True + type: str + id: + description: "" + required: false + type: str + address: + description: "" + required: true + type: str + app_server_group_ids: + description: + - This field defines the list of server groups IDs. + required: False + type: list + elements: str + enabled: + description: + - This field defines the status of the server, true or false. + required: False + type: bool + description: + description: + - This field defines the description of the server to create. + required: False + type: str + config_space: + description: + - This field defines the type of the server, DEFAULT or SIEM. + required: False + type: str + default: "DEFAULT" + choices: ["DEFAULT", "SIEM"] + state: + description: "Whether the app should be present or absent." + type: str + choices: + - present + - absent + default: present +""" + +EXAMPLES = """ +- name: Create Second Application Server + zscaler.zpacloud.zpa_application_server: + name: Example1 + description: Example1 + address: example.acme.com + enabled: true + app_server_group_ids: [] +""" + +RETURN = """ +# The newly created application server resource record. +""" + + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + deleteNone, + ZPAClientHelper, +) + + +def core(module): + state = module.params.get("state", None) + client = ZPAClientHelper(module) + server = dict() + params = [ + "id", + "name", + "description", + "address", + "enabled", + "app_server_group_ids", + "config_space", + ] + for param_name in params: + server[param_name] = module.params.get(param_name, None) + server_id = server.get("id", None) + server_name = server.get("name", None) + existing_server = None + if server_id is not None: + server_box = client.servers.get_server(server_id=server_id) + if server_box is not None: + existing_server = server_box.to_dict() + elif server_name is not None: + servers = client.servers.list_servers().to_list() + for server_ in servers: + if server_.get("name") == server_name: + existing_server = server_ + if existing_server is not None: + id = existing_server.get("id") + existing_server.update(server) + existing_server["id"] = id + if state == "present": + if existing_server is not None: + """Update""" + existing_server = deleteNone( + dict( + server_id=existing_server.get("id"), + name=existing_server.get("name"), + description=existing_server.get("description"), + address=existing_server.get("address"), + enabled=existing_server.get("enabled"), + app_server_group_ids=existing_server.get("app_server_group_ids"), + config_space=existing_server.get("config_space"), + ) + ) + existing_server = client.servers.update_server(**existing_server).to_dict() + module.exit_json(changed=True, data=existing_server) + else: + """Create""" + server = deleteNone( + dict( + name=server.get("name"), + description=server.get("description"), + address=server.get("address"), + enable=server.get("enable"), + app_server_group_ids=server.get("app_server_group_ids"), + config_space=server.get("config_space"), + ) + ) + server = client.servers.add_server(**server).to_dict() + module.exit_json(changed=True, data=server) + elif ( + state == "absent" + and existing_server is not None + and existing_server.get("id") is not None + ): + code = client.servers.delete_server(server_id=existing_server.get("id")) + if code > 299: + module.exit_json(changed=False, data=None) + module.exit_json(changed=True, data=existing_server) + module.exit_json(changed=False, data={}) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + id=dict(type="str", required=False), + name=dict(type="str", required=True), + description=dict(type="str", required=False), + address=dict(type="str", required=True), + enabled=dict(type="bool", required=False), + app_server_group_ids=dict(type="list", elements="str", required=False), + config_space=dict(type="str", required=False), + state=dict(type="str", choices=["present", "absent"], default="present"), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_application_server_info.py b/plugins/modules/zpa_application_server_info.py new file mode 100644 index 0000000..96764eb --- /dev/null +++ b/plugins/modules/zpa_application_server_info.py @@ -0,0 +1,116 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_application_server_info +short_description: Retrieve an application server information. +description: + - This module will allow the retrieval of information about an application server. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - Name of the server group. + required: false + type: str + id: + description: + - ID of the server group. + required: false + type: str +""" + +EXAMPLES = """ +- name: Gather Information Details of All Application Servers + zscaler.zpacloud.zpa_application_server_info: + +- name: Gather Information Details of an Application Server by Name + zscaler.zpacloud.zpa_application_server_info: + name: server1.acme.com + +- name: Gather Information Details of an Application Server by ID + zscaler.zpacloud.zpa_application_server_info: + id: "216196257331291921" + +""" + +RETURN = """ +# Returns information on a specified Application Server. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, +) + + +def core(module: AnsibleModule): + server_id = module.params.get("id", None) + server_name = module.params.get("name", None) + client = ZPAClientHelper(module) + servers = [] + if server_id is not None: + server_box = client.servers.get_server(server_id=server_id) + if server_box is None: + module.fail_json( + msg="Failed to retrieve Application Server ID: '%s'" % (server_id) + ) + servers = [server_box.to_dict()] + else: + servers = client.servers.list_servers().to_list() + if server_name is not None: + server_found = False + for server in servers: + if server.get("name") == server_name: + server_found = True + servers = [server] + if not server_found: + module.fail_json( + msg="Failed to retrieve Application Server Name: '%s'" + % (server_name) + ) + module.exit_json(changed=False, data=servers) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + name=dict(type="str", required=False), + id=dict(type="str", required=False), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_ba_certificate_info.py b/plugins/modules/zpa_ba_certificate_info.py new file mode 100644 index 0000000..9dc6c0d --- /dev/null +++ b/plugins/modules/zpa_ba_certificate_info.py @@ -0,0 +1,120 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_ba_certificate_info +short_description: Retrieves browser access certificate information. +description: + - This module will allow the retrieval of information about a browser access certificate. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +requirements: + - supported starting from zpa_api >= 1.0 +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - Name of the browser certificate. + required: false + type: str + id: + description: + - ID of the browser certificate. + required: false + type: str +""" + +EXAMPLES = """ +- name: Gather Details of All Browser Certificates + zscaler.zpacloud.zpa_ba_certificate_info: + +- name: Gather Details of a Specific Browser Certificates by Name + zscaler.zpacloud.zpa_ba_certificate_info: + name: crm.acme.com + +- name: Gather Details of a Specific Browser Certificates by ID + zscaler.zpacloud.zpa_ba_certificate_info: + id: "216196257331282583" +""" + +RETURN = """ +# Returns information on a specified Browser Access certificate. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, +) + + +def core(module: AnsibleModule): + certificate_id = module.params.get("id", None) + certificate_name = module.params.get("name", None) + client = ZPAClientHelper(module) + certificates = [] + if certificate_id is not None: + certificate_box = client.certificates.get_browser_access( + certificate_id=certificate_id + ) + if certificate_box is None: + module.fail_json( + msg="Failed to retrieve Browser Access Certificate ID: '%s'" + % (certificate_id) + ) + certificates = [certificate_box.to_dict()] + else: + certificates = client.certificates.list_browser_access().to_list() + if certificate_name is not None: + certificate_found = False + for certificate in certificates: + if certificate.get("name") == certificate_name: + certificate_found = True + certificates = [certificate] + if not certificate_found: + module.fail_json( + msg="Failed to retrieve Browser Access Certificate Name: '%s'" + % (certificate_name) + ) + module.exit_json(changed=False, data=certificates) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + name=dict(type="str", required=False), + id=dict(type="str", required=False), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_cloud_connector_group_info.py b/plugins/modules/zpa_cloud_connector_group_info.py new file mode 100644 index 0000000..9707e77 --- /dev/null +++ b/plugins/modules/zpa_cloud_connector_group_info.py @@ -0,0 +1,115 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_cloud_connector_group_info +short_description: Retrieves cloud connector group information. +description: + - This module will allow the retrieval of information about a cloud connector group. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - Name of the Cloud Connector Group. + required: false + type: str + id: + description: + - ID of the Cloud Connector Group. + required: false + type: str +""" + +EXAMPLES = """ +- name: Get Information Details of All Cloud Connector Groups + zscaler.zpacloud.zpa_cloud_connector_group_info: + +- name: Get Information Details of a Cloud Connector Group by Name + zscaler.zpacloud.zpa_cloud_connector_group_info: + name: zs-cc-vpc-096108eb5d9e68d71-ca-central-1a + +- name: Get Information Details of a Cloud Connector Group by ID + zscaler.zpacloud.zpa_cloud_connector_group_info: + id: "216196257331292017" +""" + +RETURN = """ +# Returns information on a specified Cloud Connector Group. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, +) + + +def core(module: AnsibleModule): + group_id = module.params.get("id", None) + group_name = module.params.get("name", None) + client = ZPAClientHelper(module) + groups = [] + if group_id is not None: + group_box = client.cloud_connector_groups.get_group(group_id=group_id) + if group_box is None: + module.fail_json( + msg="Failed to retrieve Cloud Connector Group ID: '%s'" % (group_id) + ) + groups = [group_box.to_dict()] + else: + groups = client.cloud_connector_groups.list_groups().to_list() + if group_name is not None: + group_found = False + for group in groups: + if group.get("name") == group_name: + group_found = True + groups = [group] + if not group_found: + module.fail_json( + msg="Failed to retrieve Cloud Connector Group Name: '%s'" + % (group_name) + ) + module.exit_json(changed=False, data=groups) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + name=dict(type="str", required=False), + id=dict(type="str", required=False), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_enrollement_certificate_info.py b/plugins/modules/zpa_enrollement_certificate_info.py new file mode 100644 index 0000000..0c58b34 --- /dev/null +++ b/plugins/modules/zpa_enrollement_certificate_info.py @@ -0,0 +1,132 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_enrollement_certificate_info +short_description: Retrieves enrollment certificate information. +description: + - This module will allow the retrieval of information about a Enrollment Certificate detail from the ZPA Cloud. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - Name of the browser certificate. + required: false + type: str + id: + description: + - ID of the browser certificate. + required: false + type: str +""" + +EXAMPLES = """ +- name: Gather Information Details of All Enrollment Certificates + zscaler.zpacloud.zpa_enrollment_cert_info: + +- name: Gather Information Details of the Root Enrollment Certificates by Name + zscaler.zpacloud.zpa_enrollment_cert_info: + name: "Root" + +- name: Gather Information Details of the Client Enrollment Certificates by Name + zscaler.zpacloud.zpa_enrollment_cert_info: + name: "Client" + +- name: Gather Information Details of the Connector Enrollment Certificates by Name + zscaler.zpacloud.zpa_enrollment_cert_info: + name: "Connector" + +- name: Gather Information Details of the Service Edge Enrollment Certificates by Name + zscaler.zpacloud.zpa_enrollment_cert_info: + name: "Service Edge" + +- name: Gather Information Details of the Isolation Client Enrollment Certificates by Name + zscaler.zpacloud.zpa_enrollment_cert_info: + name: "Isolation Client" +""" + +RETURN = """ +# Returns information on a specified Enrollment Certificate. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, +) + + +def core(module: AnsibleModule): + certificate_id = module.params.get("id", None) + certificate_name = module.params.get("name", None) + client = ZPAClientHelper(module) + certificates = [] + if certificate_id is not None: + certificate_box = client.certificates.get_enrolment( + certificate_id=certificate_id + ) + if certificate_box is None: + module.fail_json( + msg="Failed to retrieve Enrollment Certificate ID: '%s'" + % (certificate_id) + ) + certificates = [certificate_box.to_dict()] + else: + certificates = client.certificates.list_enrolment().to_list() + if certificate_name is not None: + certificate_found = False + for certificate in certificates: + if certificate.get("name") == certificate_name: + certificate_found = True + certificates = [certificate] + if not certificate_found: + module.fail_json( + msg="Failed to retrieve Enrollment Certificate Name: '%s'" + % (certificate_name) + ) + module.exit_json(changed=False, data=certificates) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + name=dict(type="str", required=False), + id=dict(type="str", required=False), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_idp_controller_info.py b/plugins/modules/zpa_idp_controller_info.py new file mode 100644 index 0000000..046d152 --- /dev/null +++ b/plugins/modules/zpa_idp_controller_info.py @@ -0,0 +1,114 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_idp_controller_info +short_description: Retrieves Identity Provider information. +description: + - This module will allow the retrieval of information about an Identity Provider (IdP) detail from the ZPA Cloud. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - Name of the Identity Provider. + required: false + type: str + id: + description: + - ID of the Identity Provider. + required: false + type: str +""" + +EXAMPLES = """ +- name: Get Details of All IdP Controllers + zscaler.zpacloud.zpa_idp_controller_info: + +- name: Get Details of a Specific IdP Controller by Name + zscaler.zpacloud.zpa_idp_controller_info: + name: User_IdP_Name + +- name: Get Details of a Specific IdP Controller by ID + zscaler.zpacloud.zpa_idp_controller_info: + id: "216196257331282583" +""" + +RETURN = """ +# Returns information on a specified Identity Provider. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, +) + + +def core(module: AnsibleModule): + idp_id = module.params.get("id", None) + idp_name = module.params.get("name", None) + client = ZPAClientHelper(module) + idps = [] + if idp_id is not None: + idp_box = client.idp.get_idp(idp_id=idp_id) + if idp_box is None: + module.fail_json( + msg="Failed to retrieve Identity Provider ID: '%s'" % (idp_id) + ) + idps = [idp_box.to_dict()] + else: + idps = client.idp.list_idps().to_list() + if idp_name is not None: + idp_found = False + for idp in idps: + if idp.get("name") == idp_name: + idp_found = True + idps = [idp] + if not idp_found: + module.fail_json( + msg="Failed to retrieve Identity Provider Name: '%s'" % (idp_name) + ) + module.exit_json(changed=False, data=idps) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + name=dict(type="str", required=False), + id=dict(type="str", required=False), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_lss_client_types_info.py b/plugins/modules/zpa_lss_client_types_info.py new file mode 100644 index 0000000..8773e9b --- /dev/null +++ b/plugins/modules/zpa_lss_client_types_info.py @@ -0,0 +1,117 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_lss_client_types_info +short_description: Retrieves LSS Client Types Information. +description: + - This module will allow the retrieval of LSS (Log Streaming Services) Client Types information from the ZPA Cloud. + - This can then be associated with the source_log_type parameter when creating an LSS Resource. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +requirements: + - supported starting from zpa_api >= 2.0 +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str +""" + +EXAMPLES = """ +- name: Get Details About All LSS Client Types + zscaler.zpacloud.zpa_lss_client_types_info: + register: lss_client_typeps +- debug: + msg: "{{ lss_client_typeps }}" + +""" + +RETURN = """ +data: + description: Trusted Network information + returned: success + elements: dict + type: list + sample: [ + { + "zpn_client_type_edge_connector": "Cloud Connector", + "zpn_client_type_exporter": "Web Browser", + "zpn_client_type_ip_anchoring": "ZIA Service Edge", + "zpn_client_type_machine_tunnel": "Machine Tunnel", + "zpn_client_type_slogger": "ZPA LSS", + "zpn_client_type_zapp": "Client Connector" + } + ] +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, +) + + +def core(module: AnsibleModule): + client_type_id = module.params.get("id", None) + client_type_name = module.params.get("name", None) + client = ZPAClientHelper(module) + client_types = [] + if client_type_id is not None: + lss_box = client.lss.get_client_types(client_type_id=client_type_id) + if lss_box is None: + module.fail_json( + msg="Failed to retrieve Identity Provider ID: '%s'" % (client_type_id) + ) + client_types = [lss_box.to_dict()] + else: + client_types = client.lss.get_client_types().to_list() + if client_type_name is not None: + client_type_found = False + for client_type in client_types: + if client_type.get("name") == client_type_name: + client_type_found = True + client_types = [client_type] + if not client_type_found: + module.fail_json( + msg="Failed to retrieve client type Name: '%s'" % (client_type_name) + ) + module.exit_json(changed=False, data=client_types) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + name=dict(type="str", required=False), + id=dict(type="str", required=False), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_lss_config_controller.py b/plugins/modules/zpa_lss_config_controller.py new file mode 100644 index 0000000..224261f --- /dev/null +++ b/plugins/modules/zpa_lss_config_controller.py @@ -0,0 +1,475 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_lss_config_controller +short_description: Create a LSS CONFIG. +description: + - This module create/update/delete a LSS CONFIG in the ZPA Cloud. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + config: + type: dict + required: False + description: "Name of the LSS configuration" + suboptions: + audit_message: + description: "" + type: str + required: False + description: + description: "Name of the LSS configuration" + type: str + required: False + enabled: + description: "Whether this LSS configuration is enabled or not" + type: bool + required: False + default: True + filter: + description: "Filter for the LSS configuration" + type: list + elements: str + required: False + source_log_format: + description: "Format of the log type" + type: str + required: True + choices: + - json + - csv + - tsv + id: + description: "" + type: str + name: + description: "Name of the LSS configuration" + type: str + required: True + lss_host: + description: "Host of the LSS configuration" + type: str + required: True + lss_port: + description: "Port of the LSS configuration" + type: str + required: True + source_log_type: + description: "Log type of the LSS configuration" + type: str + required: True + choices: + - app_connector_metrics + - app_connector_status + - audit_logs + - browser_access + - private_svc_edge_status + - user_activity + - user_status + use_tls: + description: "Whether TLS is enabled or not" + type: bool + required: False + default: False + app_connector_group_ids: + type: list + elements: str + required: False + description: "App Connector Group(s) to be added to the LSS configuration" + id: + type: str + description: "" + policy_rule_resource: + type: dict + description: "Object Type" + required: False + suboptions: + action: + description: "" + type: str + required: False + action_id: + description: "" + type: str + required: False + description: + description: "Object Type" + type: str + required: False + priority: + description: "" + type: str + required: False + reauth_idle_timeout: + description: "" + type: str + required: False + policy_type: + description: "" + type: str + required: False + reauth_default_rule: + description: "" + type: bool + required: False + custom_msg: + description: "" + type: str + required: False + operator: + description: "" + type: str + required: False + bypass_default_rule: + description: "" + type: bool + required: False + policy_set_id: + description: "" + type: str + required: False + default_rule: + description: "" + type: bool + required: False + name: + description: "" + type: str + required: True + reauth_timeout: + description: "" + type: str + required: False + rule_order: + description: "" + type: str + required: False + id: + description: "" + type: str + lss_default_rule: + description: "" + type: bool + required: False + conditions: + description: "" + type: list + elements: dict + required: False + suboptions: + negated: + description: "" + type: bool + required: False + operator: + description: "" + type: str + required: True + operands: + description: "" + type: list + elements: dict + required: False + suboptions: + values: + description: "" + type: list + elements: str + required: False + object_type: + description: "" + type: str + required: True + choices: ["APP", "APP_GROUP", "CLIENT_TYPE"] + state: + description: "Whether the config should be present or absent." + type: str + choices: + - present + - absent + default: present + +""" +EXAMPLES = """ +- name: LSS Controller + hosts: localhost + tasks: + - name: Create a LSS Controller + zscaler.zpacloud.zpa_lss_config_controller: + state: present + config: + name: Status + description: status + enabled: true + lss_host: 10.1.1.1 + lss_port: 20000 + format: "..." + source_log_type: "zpn_ast_auth_log" + app_connector_group_ids: + - "11111" + register: lss_controller + - name: lss_controller + debug: + msg: "{{ lss_controller }}" +""" + +RETURN = """ +# The newly created policy access rule resource record. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, + deleteNone, +) + + +def get_lss_config(id, client): + confs = client.lss.list_configs().to_list() + for lssconf in confs: + if lssconf.get("config").get("id") == id: + return lssconf + + +def core(module): + state = module.params.get("state", None) + client = ZPAClientHelper(module) + lss_config = dict() + params = ["id", "config", "app_connector_group_ids", "policy_rule_resource"] + for param_name in params: + lss_config[param_name] = module.params.get(param_name, None) + lss_config_name = lss_config.get("config", {}).get("name") + lss_config_id = lss_config.get("id") + existing_lss_config = None + if lss_config_id is not None: + existing_lss_config = client.lss.get_config(lss_id=lss_config_id) + elif lss_config_name is not None: + confs = client.lss.list_configs().to_list() + for lssconf in confs: + if lssconf.get("config").get("name") == lss_config_name: + existing_lss_config = lssconf + break + if existing_lss_config is not None: + id = existing_lss_config.get("id") + existing_lss_config.update(lss_config) + existing_lss_config["id"] = id + if state == "present": + if existing_lss_config is not None: + policy_rule_resource = existing_lss_config.get("policy_rule_resource", None) + policy_rules = [] + if policy_rule_resource is not None: + ## TODO: make sure this works + policy_rules.append(policy_rule_resource) + """Update""" + existing_lss_config = deleteNone( + dict( + lss_config_id=existing_lss_config.get("id", None), + description=existing_lss_config.get("config", dict()).get( + "description", None + ), + enabled=existing_lss_config.get("config", dict()).get( + "enabled", None + ), + filter_status_codes=existing_lss_config.get("config", dict()).get( + "filter_status_codes", None + ), + log_stream_content=existing_lss_config.get("config", dict()).get( + "log_stream_content", None + ), + source_log_format=existing_lss_config.get("config", dict()).get( + "source_log_format", None + ), + source_log_type=existing_lss_config.get("config", dict()).get( + "source_log_type", None + ), + use_tls=existing_lss_config.get("config", dict()).get( + "use_tls", None + ), + policy_rules=policy_rules, + ) + ) + lss_config = client.lss.update_lss_config(**existing_lss_config) + module.exit_json( + changed=True, data=get_lss_config(lss_config.get("id"), client) + ) + else: + """Create""" + policy_rule_resource = lss_config.get("policy_rule_resource", None) + policy_rules = [] + if policy_rule_resource is not None: + ## TODO: make sure this works + policy_rules.append(policy_rule_resource) + lss_config = deleteNone( + dict( + app_connector_group_ids=lss_config.get( + "app_connector_group_ids", None + ), + enabled=lss_config.get("config", dict()).get("enabled", None), + lss_host=lss_config.get("config", dict()).get("lss_host", None), + lss_port=lss_config.get("config", dict()).get("lss_port", None), + name=lss_config.get("config", dict()).get("name", None), + source_log_format=lss_config.get("config", dict()).get( + "source_log_format", None + ), + source_log_type=lss_config.get("config", dict()).get( + "source_log_type", None + ), + use_tls=lss_config.get("config", dict()).get("use_tls", None), + description=lss_config.get("config", dict()).get( + "description", None + ), + filter_status_codes=lss_config.get("config", dict()).get( + "filter", None + ), + log_stream_content=lss_config.get("config", dict()).get( + "log_stream_content", None + ), + policy_rules=policy_rules, + ) + ) + lss_config = client.lss.add_lss_config(**lss_config) + module.exit_json( + changed=True, data=get_lss_config(lss_config.get("id"), client) + ) + elif state == "absent" and existing_lss_config is not None: + code = client.lss.delete_lss_config(lss_id=existing_lss_config.get("id")) + if code > 299: + module.exit_json(changed=False, data=None) + module.exit_json(changed=True, data=existing_lss_config) + module.exit_json(changed=False, data={}) + + +def main(): + """Main""" + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + id=dict(type="str"), + policy_rule_resource=dict( + type="dict", + options=dict( + priority=dict(type="str", required=False), + reauth_idle_timeout=dict(type="str", required=False), + policy_type=dict(type="str", required=False), + reauth_default_rule=dict(type="bool", required=False), + custom_msg=dict(type="str", required=False), + action_id=dict(type="str", required=False), + operator=dict(type="str", required=False), + bypass_default_rule=dict(type="bool", required=False), + policy_set_id=dict(type="str", required=False), + default_rule=dict(type="bool", required=False), + action=dict(type="str", required=False), + conditions=dict( + type="list", + elements="dict", + options=dict( + negated=dict(type="bool", required=False), + operator=dict(type="str", required=True), + operands=dict( + type="list", + elements="dict", + options=dict( + values=dict( + type="list", elements="str", required=False + ), + object_type=dict( + type="str", + required=True, + choices=["APP", "APP_GROUP", "CLIENT_TYPE"], + ), + ), + required=False, + ), + ), + required=False, + ), + name=dict(type="str", required=True), + reauth_timeout=dict(type="str", required=False), + rule_order=dict(type="str", required=False), + description=dict(type="str", required=False), + id=dict(type="str"), + lss_default_rule=dict(type="bool", required=False), + ), + required=False, + ), + app_connector_group_ids=dict( + type="list", + elements="str", + required=False, + ), + config=dict( + type="dict", + options=dict( + source_log_format=dict( + type="str", + choices=[ + "json", + "csv", + "tsv", + ], + required=False, + ), + id=dict(type="str"), + name=dict(type="str", required=True), + audit_message=dict(type="str", required=False), + lss_port=dict(type="str", required=True), + lss_host=dict(type="str", required=True), + use_tls=dict(type="bool", required=False, default=False), + enabled=dict(type="bool", required=False, default=True), + description=dict(type="str", required=False), + filter=dict(type="list", elements="str", required=False), + source_log_type=dict( + type="str", + required=True, + choices=[ + "", + "app_connector_metrics", + "app_connector_status", + "audit_logs", + "browser_access", + "private_svc_edge_status", + "user_activity", + "user_status", + "web_inspection", + ], + ), + ), + required=False, + ), + state=dict(type="str", choices=["present", "absent"], default="present"), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_lss_config_controller_info.py b/plugins/modules/zpa_lss_config_controller_info.py new file mode 100644 index 0000000..914fe22 --- /dev/null +++ b/plugins/modules/zpa_lss_config_controller_info.py @@ -0,0 +1,115 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_lss_config_controller_info +short_description: Retrieves LSS Config controller information. +description: + - This module will allow the retrieval of information about a LSS Config controlle. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - Name of the LSS Config controlle. + required: false + type: str + id: + description: + - ID of the LSS Config controlle. + required: false + type: str +""" + +EXAMPLES = """ +- name: Get Information Details of All Cloud lss_configs + zscaler.zpacloud.zpa_lss_config_controller_info: + +- name: Get Information Details of a LSS Config controlle by Name + zscaler.zpacloud.zpa_lss_config_controller_info: + name: zs-cc-vpc-096108eb5d9e68d71-ca-central-1a + +- name: Get Information Details of a LSS Config controlle by ID + zscaler.zpacloud.zpa_lss_config_controller_info: + id: "216196257331292017" +""" + +RETURN = """ +# Returns information on a specified LSS Config controlle. +""" + +from re import T +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, +) + + +def core(module): + lss_config_name = module.params.get("name", None) + lss_config_id = module.params.get("id", None) + client = ZPAClientHelper(module) + lss_configs = [] + if lss_config_id is not None: + lss_config = client.lss.get_config(lss_id=lss_config_id) + if lss_config is None: + module.fail_json(msg="Failed to retrieve lss_config ID: '%s'" % (id)) + lss_configs = [lss_config] + elif lss_config_name is not None: + lss_configs_ = client.lss.list_configs().to_list() + found = False + for k in lss_configs_: + if k.get("config").get("name") == lss_config_name: + lss_configs = [k] + found = True + break + if not found: + module.fail_json( + msg="Failed to retrieve lss_config Name: '%s'" % (lss_config_name) + ) + else: + lss_configs = client.lss.list_configs().to_list() + module.exit_json(changed=False, data=lss_configs) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + name=dict(type="str", required=False), + id=dict(type="str", required=False), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_lss_config_log_types_formats_info.py b/plugins/modules/zpa_lss_config_log_types_formats_info.py new file mode 100644 index 0000000..ea830ab --- /dev/null +++ b/plugins/modules/zpa_lss_config_log_types_formats_info.py @@ -0,0 +1,111 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_lss_config_log_types_formats_info +short_description: Retrieves LSS Log formats Information. +description: + - This module will allow the retrieval of LSS (Log Streaming Services) Log formats information from the ZPA Cloud. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + log_type: + description: + - Log type + required: true + choices: ["zpn_trans_log", "zpn_auth_log", "zpn_ast_auth_log", "zpn_http_trans_log", "zpn_audit_log", "zpn_ast_comprehensive_stats", "zpn_sys_auth_log"] + type: str +""" + +EXAMPLES = """ + - name: Gather LSS Log types formats + zscaler.zpacloud.zpa_lss_config_log_types_formats_info: + log_type: zpn_trans_log + register: log_types_formats + - name: log_types_formats + debug: + msg: "{{ log_types_formats }}" +""" + +RETURN = """ +data: + description: LSS Log formats + returned: success + elements: dict + type: list + sample: [ + { + "csv": "...", + "json": "...", + "tsv": "..." + } + ] +""" + +from re import T +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, +) + + +def core(module): + client = ZPAClientHelper(module) + log_type = module.params.get("log_type", None) + lss_log_formats = client.lss.get_log_formats() + log_format = lss_log_formats.get(log_type, None) + module.exit_json(changed=False, data=log_format) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + log_type=dict( + type="str", + required=True, + choices=[ + "zpn_trans_log", + "zpn_auth_log", + "zpn_ast_auth_log", + "zpn_http_trans_log", + "zpn_audit_log", + "zpn_ast_comprehensive_stats", + "zpn_sys_auth_log", + ], + ), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_lss_config_status_codes_info.py b/plugins/modules/zpa_lss_config_status_codes_info.py new file mode 100644 index 0000000..4bd4d53 --- /dev/null +++ b/plugins/modules/zpa_lss_config_status_codes_info.py @@ -0,0 +1,194 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_lss_config_status_codes_info +short_description: Retrieves LSS Status Codes Information. +description: + - This module will allow the retrieval of LSS (Log Streaming Services) Status Codes information from the ZPA Cloud. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str +""" + +EXAMPLES = """ +- name: Get Details About All LSS Status Codes + zscaler.zpacloud.zpa_lss_config_status_codes_info: + register: lss_status_codes +- debug: + msg: "{{ lss_status_codes }}" +""" + +RETURN = """ +data: + description: LSS Status Codes + returned: success + elements: dict + type: list + sample: + [ + { + "zpn_ast_auth_log": + { + "ZPN_STATUS_AUTHENTICATED": + { + "adminAction": "NA", + "errorType": "NA", + "name": "Authenticated", + "status": "Success", + }, + "ZPN_STATUS_AUTH_FAILED": + { + "adminAction": "NA", + "errorType": "NA", + "name": "Authentication failed", + "status": "Error", + }, + "ZPN_STATUS_DISCONNECTED": + { + "adminAction": "NA", + "errorType": "NA", + "name": "Disconnected", + "status": "Success", + }, + }, + "zpn_auth_log": + { + "ZPN_STATUS_AUTHENTICATED": + { + "adminAction": "NA", + "errorType": "NA", + "name": "Authenticated", + "status": "Success", + }, + "ZPN_STATUS_AUTH_FAILED": + { + "adminAction": "NA", + "errorType": "NA", + "name": "Authentication failed", + "status": "Error", + }, + "ZPN_STATUS_DISCONNECTED": + { + "adminAction": "NA", + "errorType": "NA", + "name": "Disconnected", + "status": "Success", + }, + }, + "zpn_sys_auth_log": + { + "ZPN_STATUS_AUTHENTICATED": + { + "adminAction": "NA", + "errorType": "NA", + "name": "Authenticated", + "status": "Success", + }, + "ZPN_STATUS_AUTH_FAILED": + { + "adminAction": "NA", + "errorType": "NA", + "name": "Authentication failed", + "status": "Error", + }, + "ZPN_STATUS_DISCONNECTED": + { + "adminAction": "NA", + "errorType": "NA", + "name": "Disconnected", + "status": "Success", + }, + }, + "zpn_trans_log": + { + "APP_NOT_AVAILABLE": + { + "adminAction": "NA", + "errorType": "InternalError", + "name": "CA: Application is not available", + "status": "Error", + }, + "AST_MT_SETUP_TIMEOUT_CANNOT_CONN_TO_SERVER": + { + "adminAction": "Check connectivity to server", + "errorType": "ActionableError", + "name": "AC: Connection request to server timed out", + "status": "Error", + }, + "BRK_MT_AUTH_SAML_CANNOT_ADD_ATTR_TO_HEAP": + { + "adminAction": "NA", + "errorType": "InternalError", + "name": "SE: Authentication failed due to insufficient memory", + "status": "Error", + }, + "BRK_MT_AUTH_SAML_DECODE_FAIL": + { + "adminAction": "NA", + "errorType": "InternalError", + "status": "Error", + }, + "BRK_MT_AUTH_SAML_FAILURE": + { + "adminAction": "NA", + "errorType": "ActionableError", + "name": "SE: Authentication unsuccessful", + "status": "Error", + }, + }, + }, + ] + +""" + +from re import T +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, +) + + +def core(module): + client = ZPAClientHelper(module) + lss_status_codes = client.lss.get_status_codes(log_type="all") + module.exit_json(changed=False, data=lss_status_codes) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_machine_group_info.py b/plugins/modules/zpa_machine_group_info.py new file mode 100644 index 0000000..845a20d --- /dev/null +++ b/plugins/modules/zpa_machine_group_info.py @@ -0,0 +1,114 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_machine_group_info +short_description: Retrieves machine group information. +description: + - This module will allow the retrieval of information about a machine group detail from the ZPA Cloud. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - Name of the machine group. + required: false + type: str + id: + description: + - ID of the machine group. + required: false + type: str +""" + +EXAMPLES = """ +- name: Get Details of All Machine Groups + zscaler.zpacloud.zpa_machine_group_info: + +- name: Get Details of a Specific Machine Group by Name + zscaler.zpacloud.zpa_machine_group_info: + name: "Corp_Machine_Group" + +- name: Get Details of a Specific Machine Group by ID + zscaler.zpacloud.zpa_machine_group_info: + id: "216196257331282583" +""" + +RETURN = """ +# Returns information on a specified Machine Group. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, +) + + +def core(module: AnsibleModule): + group_id = module.params.get("id", None) + group_name = module.params.get("name", None) + client = ZPAClientHelper(module) + groups = [] + if group_id is not None: + group_box = client.machine_groups.get_group(group_id=group_id) + if group_box is None: + module.fail_json( + msg="Failed to retrieve Machine Group ID: '%s'" % (group_id) + ) + groups = [group_box.to_dict()] + else: + groups = client.machine_groups.list_groups().to_list() + if group_name is not None: + group_found = False + for group in groups: + if group.get("name") == group_name: + group_found = True + groups = [group] + if not group_found: + module.fail_json( + msg="Failed to retrieve Machine Group Name: '%s'" % (group_name) + ) + module.exit_json(changed=False, data=groups) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + name=dict(type="str", required=False), + id=dict(type="str", required=False), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_policy_access_rule.py b/plugins/modules/zpa_policy_access_rule.py new file mode 100644 index 0000000..7b97153 --- /dev/null +++ b/plugins/modules/zpa_policy_access_rule.py @@ -0,0 +1,420 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_policy_access_rule +short_description: Create a Policy Access Rule +description: + - This module create/update/delete a Policy Access Rule in the ZPA Cloud. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + action: + description: + - This is for providing the rule action. + type: str + required: false + choices: + - ALLOW + - DENY + action_id: + type: str + required: false + description: + - This field defines the description of the server. + priority: + description: "" + type: str + required: false + id: + type: str + description: "" + default_rule_name: + type: str + description: "" + description: + type: str + description: "" + policy_type: + description: "" + type: str + required: false + rule_order: + description: "" + type: str + required: false + default_rule: + description: + - This is for providing a customer message for the user. + type: bool + required: false + operator: + description: + - This denotes the operation type. + type: str + required: false + choices: + - AND + - OR + app_connector_groups: + description: + - List of the app connector group IDs. + type: list + elements: dict + required: false + suboptions: + name: + required: false + type: str + description: "" + id: + required: true + type: str + description: "" + app_server_groups: + type: list + elements: dict + required: false + description: + - List of the server group IDs. + suboptions: + name: + required: false + type: str + description: "" + id: + required: true + type: str + description: "" + custom_msg: + description: + - This is for providing a customer message for the user. + type: str + required: false + lss_default_rule: + description: "" + type: bool + required: False + name: + description: + - This is the name of the policy. + type: str + required: True + conditions: + type: list + elements: dict + required: False + description: "" + suboptions: + id: + description: "" + type: str + negated: + description: "" + type: bool + required: False + operator: + description: "" + type: str + required: True + choices: ["AND", "OR"] + operands: + required: False + description: "" + type: list + elements: dict + suboptions: + id: + description: "" + type: str + idp_id: + description: "" + type: str + required: False + name: + description: "" + type: str + required: False + lhs: + description: "" + type: str + required: True + rhs: + description: "" + type: str + required: False + rhs_list: + description: "" + type: list + elements: str + required: False + object_type: + description: "" + type: str + required: True + choices: + - APP + - APP_GROUP + - SAML + - IDP + - CLIENT_TYPE + - TRUSTED_NETWORK + - MACHINE_GRP + - POSTURE + - SCIM + - SCIM_GROUP + - EDGE_CONNECTOR_GROUP + state: + description: "Whether the app should be present or absent." + type: str + choices: + - present + - absent + default: present +""" + +EXAMPLES = """ +- name: Access Policy - Intranet Web Apps + zscaler.zpacloud.zpa_policy_access_rule: + name: "Intranet Web Apps" + description: "Intranet Web Apps" + action: "ALLOW" + rule_order: 1 + operator: "AND" + conditions: + - negated: false + operator: "OR" + operands: + - name: "app_seg_intranet" + object_type: "APP" + lhs: "id" + rhs: "{{ app_seg_intranet.data.id }}" + - negated: false + operator: "OR" + operands: + - name: "sg_seg_intranet" + object_type: "APP_GROUP" + lhs: "id" + rhs: "{{ seg_intranet.data.id }}" + - negated: false + operator: "OR" + operands: + - name: "engineering_group" + object_type: "SCIM_GROUP" + lhs: "{{ user_okta.data[0].id }}" + rhs: "{{ engineering_group.data[0].id }}" +""" + +RETURN = """ +# The newly created policy access rule resource record. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, + deleteNone, +) + + +def map_conditions(conditions_obj): + result = [] + for condition in conditions_obj: + operands = condition.get("operands") + if operands is not None and isinstance(operands, list): + for op in operands: + if ( + op.get("object_type", None) is not None + and op.get("lhs", None) is not None + and op.get("rhs", None) is not None + ): + operand = ( + op.get("object_type", None), + op.get("lhs", None), + op.get("rhs", None), + ) + result.append(operand) + return result + + +def core(module): + state = module.params.get("state", None) + client = ZPAClientHelper(module) + policy_rule_id = module.params.get("id", None) + policy_rule_name = module.params.get("name", None) + policy = dict() + params = [ + "default_rule", + "description", + "policy_type", + "custom_msg", + "id", + "lss_default_rule", + "action_id", + "name", + "app_connector_groups", + "action", + "priority", + "operator", + "rule_order", + "conditions", + "app_server_groups", + ] + for param_name in params: + policy[param_name] = module.params.get(param_name, None) + existing_policy = None + if policy_rule_id is not None: + existing_policy = client.policies.get_rule( + policy_type="access", rule_id=policy_rule_id + ) + elif policy_rule_name is not None: + rules = client.policies.list_rules(policy_type="access").to_list() + for rule in rules: + if rule.get("name") == policy_rule_name: + existing_policy = rule + break + if existing_policy is not None: + id = existing_policy.get("id") + existing_policy.update(policy) + existing_policy["id"] = id + if state == "present": + if existing_policy is not None: + """Update""" + existing_policy = deleteNone( + dict( + policy_type="access", + rule_id=existing_policy.get("id", None), + name=existing_policy.get("name", None), + description=existing_policy.get("description", None), + action=existing_policy.get("action", "").upper(), + conditions=map_conditions(existing_policy.get("conditions", [])), + custom_msg=existing_policy.get("custom_msg", None), + ) + ) + existing_policy = client.policies.update_rule(**existing_policy) + module.exit_json(changed=True, data=existing_policy) + else: + """Create""" + policy = deleteNone( + dict( + name=policy.get("name", None), + description=policy.get("description", None), + action=policy.get("action", None), + conditions=map_conditions(policy.get("conditions", [])), + custom_msg=policy.get("custom_msg", None), + ) + ) + policy = client.policies.add_access_rule(**policy) + module.exit_json(changed=False, data=policy) + elif state == "absent" and existing_policy is not None: + code = client.policies.delete_rule( + policy_type="access", rule_id=existing_policy.get("id") + ) + if code > 299: + module.exit_json(changed=False, data=None) + module.exit_json(changed=True, data=existing_policy) + module.exit_json(changed=False, data={}) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + id_name_spec = dict( + type="list", + elements="dict", + options=dict( + id=dict(type="str", required=True), name=dict(type="str", required=False) + ), + required=False, + ) + argument_spec.update( + id=dict(type="str"), + name=dict(type="str", required=True), + description=dict(type="str", required=False), + policy_type=dict(type="str", required=False), + custom_msg=dict(type="str", required=False), + lss_default_rule=dict(type="bool", required=False), + app_connector_groups=id_name_spec, + action=dict( + type="str", required=False, choices=["allow", "deny", "ALLOW", "DENY"] + ), + operator=dict(type="str", required=False, choices=["AND", "OR"]), + rule_order=dict(type="str", required=False), + conditions=dict( + type="list", + elements="dict", + options=dict( + id=dict(type="str"), + negated=dict(type="bool", required=False), + operator=dict(type="str", required=True, choices=["AND", "OR"]), + operands=dict( + type="list", + elements="dict", + options=dict( + id=dict(type="str"), + idp_id=dict(type="str", required=False), + name=dict(type="str", required=False), + lhs=dict(type="str", required=True), + rhs=dict(type="str", required=False), + rhs_list=dict(type="list", elements="str", required=False), + object_type=dict( + type="str", + required=True, + choices=[ + "APP", + "APP_GROUP", + "SAML", + "IDP", + "CLIENT_TYPE", + "trusted_network", + "MACHINE_GRP", + "POSTURE", + "SCIM", + "SCIM_GROUP", + "EDGE_CONNECTOR_GROUP", + "COUNTRY_CODE", + "PLATFORM", + ], + ), + ), + required=False, + ), + ), + required=False, + ), + app_server_groups=id_name_spec, + state=dict(type="str", choices=["present", "absent"], default="present"), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_policy_access_rule_info.py b/plugins/modules/zpa_policy_access_rule_info.py new file mode 100644 index 0000000..b6fa582 --- /dev/null +++ b/plugins/modules/zpa_policy_access_rule_info.py @@ -0,0 +1,117 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_policy_access_rule_info +short_description: Retrieves policy access rule information. +description: + - This module will allow the retrieval of information about a policy access rule. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +requirements: + - supported starting from zpa_api >= 1.0 +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - Name of the policy rule. + required: false + type: str + id: + description: + - ID of the policy rule. + required: false + type: str +""" + +EXAMPLES = """ +- name: Get Details of All Policy Access Rules + zscaler.zpacloud.zpa_policy_access_rule_info: +- name: Get Details of a Policy Access Rule by Name + zscaler.zpacloud.zpa_policy_access_rule_info: + name: "Policy Access Rule - Example" +- name: Get Details of a Policy Access Rule by ID + zscaler.zpacloud.zpa_policy_access_rule_info: + id: "216196257331291979" +""" + +RETURN = """ +# Returns information on a specified Policy Access Rule. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, + deleteNone, +) + + +def core(module): + policy_rule_name = module.params.get("name", None) + policy_rule_id = module.params.get("id", None) + client = ZPAClientHelper(module) + policy_rules = [] + if policy_rule_id is not None: + policy_rule = client.policies.get_rule( + policy_type="access", rule_id=policy_rule_id + ) + if policy_rule is None: + module.fail_json(msg="Failed to retrieve policy rule ID: '%s'" % (id)) + policy_rules = [policy_rule] + elif policy_rule_name is not None: + rules = client.policies.list_rules(policy_type="access").to_list() + found = False + for rule in rules: + if rule.get("name") == policy_rule_name: + policy_rules = [rule] + found = True + break + if not found: + module.fail_json( + msg="Failed to retrieve policy rule Name: '%s'" % (policy_rule_name) + ) + else: + policy_rules = client.policies.list_rules(policy_type="access").to_list() + module.exit_json(changed=False, data=policy_rules) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + name=dict(type="str", required=False), + id=dict(type="str", required=False), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_policy_forwarding_rule.py b/plugins/modules/zpa_policy_forwarding_rule.py new file mode 100644 index 0000000..1137946 --- /dev/null +++ b/plugins/modules/zpa_policy_forwarding_rule.py @@ -0,0 +1,389 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_policy_forwarding_rule +short_description: Create a Policy Forwarding Rule. +description: + - This module will create, update or delete a specific Policy Forwarding Rule +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + id: + description: "" + type: str + name: + description: "" + type: str + required: True + description: + description: "" + type: str + required: False + action: + description: "" + type: str + required: False + choices: ["INTERCEPT", "INTERCEPT_ACCESSIBLE", "BYPASS"] + default: INTERCEPT + default_rule: + description: "" + type: bool + required: False + default_rule_name: + description: "" + type: str + required: False + custom_msg: + description: "" + type: str + required: False + bypass_default_rule: + description: "" + type: bool + required: False + operator: + description: "" + type: str + required: False + choices: ["AND", "OR"] + policy_type: + description: "" + type: str + required: False + priority: + description: "" + type: str + required: False + rule_order: + description: "" + type: str + required: False + conditions: + description: "" + type: list + elements: dict + required: False + suboptions: + id: + description: "" + type: str + negated: + description: "" + type: bool + required: False + operator: + description: "" + type: str + required: True + choices: ["AND", "OR"] + operands: + description: "" + type: list + elements: dict + required: False + suboptions: + id: + description: "" + type: str + idp_id: + description: "" + type: str + required: False + name: + description: "" + type: str + required: False + lhs: + description: "" + type: str + required: True + rhs: + description: "" + type: str + required: False + rhs_list: + description: "" + type: list + elements: str + required: False + object_type: + description: "" + type: str + required: True + choices: + [ + "APP", + "APP_GROUP", + "SAML", + "IDP", + "SCIM", + "SCIM_GROUP", + "CLIENT_TYPE", + "TRUSTED_NETWORK", + "MACHINE_GRP", + "POSTURE", + "EDGE_CONNECTOR_GROUP", + ] + state: + description: "" + type: str + choices: ["present", "absent"] + default: present +""" + +EXAMPLES = """ +- name: Policy Forwarding Rule - Example + zscaler.zpacloud.zpa_policy_forwarding_rule: + name: "Policy Forwarding Rule - Example" + description: "Policy Forwarding Rule - Example" + action: "BYPASS" + rule_order: 1 + operator: "AND" + conditions: + - negated: false + operator: "OR" + operands: + - name: "app_segment" + object_type: "APP" + lhs: "id" + rhs: "216196257331292105" + - negated: false + operator: "OR" + operands: + - name: "segment_group" + object_type: "APP_GROUP" + lhs: "id" + rhs: "216196257331292103" + - negated: false + operator: "OR" + operands: + - name: "zpn_client_type_exporter" + object_type: "CLIENT_TYPE" + lhs: "id" + rhs: "zpn_client_type_exporter" + - name: "zpn_client_type_browser_isolation" + object_type: "CLIENT_TYPE" + lhs: "id" + rhs: "zpn_client_type_browser_isolation" + - name: "zpn_client_type_zapp" + object_type: "CLIENT_TYPE" + lhs: "id" + rhs: "zpn_client_type_zapp" + - negated: false + operator: "OR" + operands: + - name: "CrowdStrike_ZPA_ZTA_80" + object_type: "POSTURE" + lhs: "{{ postures.data[0].posture_udid }}" + rhs: "false" +""" + +RETURN = """ +# The newly created access client forwarding policy rule resource record. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, + deleteNone, +) + + +def map_conditions(conditions_obj): + result = [] + for condition in conditions_obj: + operands = condition.get("operands") + if operands is not None and isinstance(operands, list): + for op in operands: + if ( + op.get("object_type", None) is not None + and op.get("lhs", None) is not None + and op.get("rhs", None) is not None + ): + operand = ( + op.get("object_type", None), + op.get("lhs", None), + op.get("rhs", None), + ) + result.append(operand) + return result + + +def core(module): + state = module.params.get("state", None) + client = ZPAClientHelper(module) + policy_rule_id = module.params.get("id", None) + policy_rule_name = module.params.get("name", None) + policy = dict() + params = [ + "id", + "name", + "description", + "policy_type", + "action", + "operator", + "rule_order", + "conditions", + ] + for param_name in params: + policy[param_name] = module.params.get(param_name, None) + existing_policy = None + if policy_rule_id is not None: + existing_policy = client.policies.get_rule( + policy_type="client_forwarding", rule_id=policy_rule_id + ) + elif policy_rule_name is not None: + rules = client.policies.list_rules(policy_type="client_forwarding").to_list() + for rule in rules: + if rule.get("name") == policy_rule_name: + existing_policy = rule + break + if existing_policy is not None: + id = existing_policy.get("id") + existing_policy.update(policy) + existing_policy["id"] = id + if state == "present": + if existing_policy is not None: + """Update""" + existing_policy = deleteNone( + dict( + policy_type="client_forwarding", + rule_id=existing_policy.get("id", None), + name=existing_policy.get("name", None), + description=existing_policy.get("description", None), + action=existing_policy.get("action", "").upper(), + conditions=map_conditions(existing_policy.get("conditions", [])), + ) + ) + existing_policy = client.policies.update_rule(**existing_policy) + module.exit_json(changed=True, data=existing_policy) + else: + """Create""" + policy = deleteNone( + dict( + name=policy.get("name", None), + description=policy.get("description", None), + action=policy.get("action", None), + conditions=map_conditions(policy.get("conditions", [])), + ) + ) + policy = client.policies.add_client_forwarding_rule(**policy) + module.exit_json(changed=False, data=policy) + elif state == "absent" and existing_policy is not None: + code = client.policies.delete_rule( + policy_type="client_forwarding", rule_id=existing_policy.get("id") + ) + if code > 299: + module.exit_json(changed=False, data=None) + module.exit_json(changed=True, data=existing_policy) + module.exit_json(changed=False, data={}) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + id_name_spec = dict( + type="list", + elements="dict", + options=dict( + id=dict(type="str", required=True), name=dict(type="str", required=False) + ), + required=False, + ) + argument_spec.update( + id=dict(type="str"), + name=dict(type="str", required=True), + description=dict(type="str", required=False), + policy_type=dict(type="str", required=False), + action=dict( + type="str", + required=False, + choices=[ + "bypass", + "BYPASS", + "intercept", + "INTERCEPT", + "intercept_accessible", + "INTERCEPT_ACCESSIBLE", + ], + ), + operator=dict(type="str", required=False, choices=["AND", "OR"]), + rule_order=dict(type="str", required=False), + conditions=dict( + type="list", + elements="dict", + options=dict( + id=dict(type="str"), + negated=dict(type="bool", required=False), + operator=dict(type="str", required=True, choices=["AND", "OR"]), + operands=dict( + type="list", + elements="dict", + options=dict( + id=dict(type="str"), + idp_id=dict(type="str", required=False), + name=dict(type="str", required=False), + lhs=dict(type="str", required=True), + rhs=dict(type="str", required=False), + rhs_list=dict(type="list", elements="str", required=False), + object_type=dict( + type="str", + required=True, + choices=[ + "APP", + "APP_GROUP", + "SAML", + "IDP", + "SCIM", + "SCIM_GROUP", + "CLIENT_TYPE", + "TRUSTED_NETWORK", + "MACHINE_GRP", + "POSTURE", + "EDGE_CONNECTOR_GROUP", + ], + ), + ), + required=False, + ), + ), + required=False, + ), + app_server_groups=id_name_spec, + state=dict(type="str", choices=["present", "absent"], default="present"), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_policy_forwarding_rule_info.py b/plugins/modules/zpa_policy_forwarding_rule_info.py new file mode 100644 index 0000000..9d21e67 --- /dev/null +++ b/plugins/modules/zpa_policy_forwarding_rule_info.py @@ -0,0 +1,119 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_policy_forwarding_rule_info +short_description: Retrieves policy forwarding rule information. +description: + - This module will allow the retrieval of information about a policy forwarding rule. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - Name of the policy forwarding rule. + required: false + type: str + id: + description: + - ID of the policy forwarding rule. + required: false + type: str +""" + +EXAMPLES = """ +- name: Get Information About All Policy Forwarding Rules + zscaler.zpacloud.zpa_policy_forwarding_rule_info: + +- name: Get information About Forwarding Rules by Name + zscaler.zpacloud.zpa_policy_forwarding_rule_info: + name: "All Other Services" + +- name: Get information About Forwarding Rules by ID + zscaler.zpacloud.zpa_policy_forwarding_rule_info: + id: "216196257331292020" +""" + +RETURN = """ +# Returns information on a specified policy forwarding rule. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, + deleteNone, +) + + +def core(module): + policy_rule_name = module.params.get("name", None) + policy_rule_id = module.params.get("id", None) + client = ZPAClientHelper(module) + policy_rules = [] + if policy_rule_id is not None: + policy_rule = client.policies.get_rule( + policy_type="client_forwarding", rule_id=policy_rule_id + ) + if policy_rule is None: + module.fail_json(msg="Failed to retrieve policy rule ID: '%s'" % (id)) + policy_rules = [policy_rule] + elif policy_rule_name is not None: + rules = client.policies.list_rules(policy_type="client_forwarding").to_list() + found = False + for rule in rules: + if rule.get("name") == policy_rule_name: + policy_rules = [rule] + found = True + break + if not found: + module.fail_json( + msg="Failed to retrieve policy rule Name: '%s'" % (policy_rule_name) + ) + else: + policy_rules = client.policies.list_rules( + policy_type="client_forwarding" + ).to_list() + module.exit_json(changed=False, data=policy_rules) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + name=dict(type="str", required=False), + id=dict(type="str", required=False), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_policy_timeout_rule.py b/plugins/modules/zpa_policy_timeout_rule.py new file mode 100644 index 0000000..fa4ea12 --- /dev/null +++ b/plugins/modules/zpa_policy_timeout_rule.py @@ -0,0 +1,377 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +DOCUMENTATION = """ +--- +module: zpa_policy_timeout_rule +short_description: Create a Policy Timeout Rule +description: + - This module create/update/delete a Policy Timeout Rule in the ZPA Cloud. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + action: + description: + - This is for providing the rule action. + type: str + required: false + choices: + - RE_AUTH + priority: + type: str + required: false + description: "" + reauth_default_rule: + type: bool + required: false + description: "" + id: + description: "" + type: str + required: false + policy_type: + description: "" + type: str + required: false + rule_order: + description: "" + type: str + required: false + default_rule: + description: + - This is for providing a customer message for the user. + type: bool + required: false + operator: + description: + - This denotes the operation type. + type: str + required: false + choices: + - AND + - OR + description: + description: + - This is the description of the access policy. + type: str + required: false + custom_msg: + description: + - This is for providing a customer message for the user. + type: str + required: false + name: + type: str + required: True + description: + - This is the name of the timeout policy. + default_rule_name: + type: str + required: false + description: "" + reauth_idle_timeout: + type: str + required: false + description: "" + reauth_timeout: + type: str + required: false + description: "" + conditions: + type: list + elements: dict + required: False + description: "" + suboptions: + id: + description: "" + type: str + negated: + description: "" + type: bool + required: False + operator: + description: "" + type: str + required: True + operands: + description: "" + type: list + elements: dict + required: False + suboptions: + id: + description: "" + type: str + idp_id: + description: "" + type: str + required: False + name: + description: "" + type: str + required: False + lhs: + description: "" + type: str + required: True + rhs: + description: "" + type: str + required: False + object_type: + description: "" + type: str + required: True + state: + description: "Whether the app should be present or absent." + type: str + choices: + - present + - absent + default: present + +""" + +EXAMPLES = """ +- name: "Policy Timeout Rule - Example" + zscaler.zpacloud.zpa_policy_timeout_rule: + name: "Policy Timeout Rule - Example" + description: "Policy Timeout Rule - Example" + action: "RE_AUTH" + rule_order: 1 + reauth_idle_timeout: 600 + reauth_timeout: 172800 + operator: "AND" + conditions: + - negated: false + operator: "OR" + operands: + - name: "app_segment" + object_type: "APP" + lhs: "id" + rhs: "216196257331292105" + - negated: false + operator: "OR" + operands: + - name: "segment_group" + object_type: "APP_GROUP" + lhs: "id" + rhs: "216196257331292103" + - negated: false + operator: "OR" + operands: + - name: "zpn_client_type_exporter" + object_type: "CLIENT_TYPE" + lhs: "id" + rhs: "zpn_client_type_exporter" + - name: "zpn_client_type_browser_isolation" + object_type: "CLIENT_TYPE" + lhs: "id" + rhs: "zpn_client_type_browser_isolation" + - name: "zpn_client_type_zapp" + object_type: "CLIENT_TYPE" + lhs: "id" + rhs: "zpn_client_type_zapp" + - negated: false + operator: "OR" + operands: + - name: "CrowdStrike_ZPA_ZTA_40" + object_type: "POSTURE" + lhs: "13ba3d97-aefb-4acc-9e54-6cc230dee4a5" + rhs: "true" +""" + +RETURN = """ +# The newly created policy access timeout rule resource record. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, + deleteNone, +) + + +def map_conditions(conditions_obj): + result = [] + for condition in conditions_obj: + operands = condition.get("operands") + if operands is not None and isinstance(operands, list): + for op in operands: + if ( + op.get("object_type", None) is not None + and op.get("lhs", None) is not None + and op.get("rhs", None) is not None + ): + operand = ( + op.get("object_type", None), + op.get("lhs", None), + op.get("rhs", None), + ) + result.append(operand) + return result + + +def core(module): + state = module.params.get("state", None) + client = ZPAClientHelper(module) + policy_rule_id = module.params.get("id", None) + policy_rule_name = module.params.get("name", None) + policy = dict() + params = [ + "id", + "name", + "description", + "policy_type", + "action", + "reauth_idle_timeout", + "reauth_timeout", + "operator", + "rule_order", + "conditions", + ] + for param_name in params: + policy[param_name] = module.params.get(param_name, None) + existing_policy = None + if policy_rule_id is not None: + existing_policy = client.policies.get_rule( + policy_type="timeout", rule_id=policy_rule_id + ) + elif policy_rule_name is not None: + rules = client.policies.list_rules(policy_type="timeout").to_list() + for rule in rules: + if rule.get("name") == policy_rule_name: + existing_policy = rule + break + if existing_policy is not None: + id = existing_policy.get("id") + existing_policy.update(policy) + existing_policy["id"] = id + if state == "present": + if existing_policy is not None: + """Update""" + existing_policy = deleteNone( + dict( + policy_type="timeout", + rule_id=existing_policy.get("id", None), + name=existing_policy.get("name", None), + description=existing_policy.get("description", None), + action=existing_policy.get("action", "").upper(), + reauth_idle_timeout=existing_policy.get( + "reauth_idle_timeout", None + ), + reauth_timeout=existing_policy.get("reauth_timeout", None), + conditions=map_conditions(existing_policy.get("conditions", [])), + ) + ) + existing_policy = client.policies.update_rule(**existing_policy) + module.exit_json(changed=True, data=existing_policy) + else: + """Create""" + policy = deleteNone( + dict( + name=policy.get("name", None), + description=policy.get("description", None), + action=policy.get("action", None), + reauth_idle_timeout=policy.get("reauth_idle_timeout", None), + reauth_timeout=policy.get("reauth_timeout", None), + conditions=map_conditions(policy.get("conditions", [])), + ) + ) + policy = client.policies.add_timeout_rule(**policy) + module.exit_json(changed=False, data=policy) + elif state == "absent" and existing_policy is not None: + code = client.policies.delete_rule( + policy_type="timeout", rule_id=existing_policy.get("id") + ) + if code > 299: + module.exit_json(changed=False, data=None) + module.exit_json(changed=True, data=existing_policy) + module.exit_json(changed=False, data={}) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + id=dict(type="str"), + name=dict(type="str", required=True), + description=dict(type="str", required=False), + policy_type=dict(type="str", required=False), + action=dict(type="str", required=False, choices=["RE_AUTH"]), + reauth_idle_timeout=dict(type="str", required=True), + reauth_timeout=dict(type="str", required=True), + operator=dict(type="str", required=False, choices=["AND", "OR"]), + rule_order=dict(type="str", required=False), + conditions=dict( + type="list", + elements="dict", + options=dict( + id=dict(type="str"), + negated=dict(type="bool", required=False), + operator=dict(type="str", required=True, choices=["AND", "OR"]), + operands=dict( + type="list", + elements="dict", + options=dict( + id=dict(type="str"), + idp_id=dict(type="str", required=False), + name=dict(type="str", required=False), + lhs=dict(type="str", required=True), + rhs=dict(type="str", required=False), + object_type=dict( + type="str", + required=True, + choices=[ + "APP", + "APP_GROUP", + "CLIENT_TYPE", + "SAML", + "IDP", + "SCIM", + "SCIM_GROUP", + "TRUSTED_NETWORK", + "EDGE_CONNECTOR_GROUP", + "POSTURE", + ], + ), + ), + required=False, + ), + ), + required=False, + ), + state=dict(type="str", choices=["present", "absent"], default="present"), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_policy_timeout_rule_info.py b/plugins/modules/zpa_policy_timeout_rule_info.py new file mode 100644 index 0000000..42b89a7 --- /dev/null +++ b/plugins/modules/zpa_policy_timeout_rule_info.py @@ -0,0 +1,115 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_policy_timeout_rule_info +short_description: Retrieves policy timeout rule information. +description: + - This module will allow the retrieval of information about a policy timeout rule. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - Name of the policy timeout rule. + required: false + type: str + id: + description: + - ID of the policy timeout rule. + required: false + type: str +""" + +EXAMPLES = """ +- name: Gather information about all policy rules + zscaler.zpacloud.zpa_policy_timeout_rule_info: +- name: Get Information About a Specific Timeout Rule by Name + zscaler.zpacloud.zpa_policy_timeout_rule_info: + name: "Example" +- name: Get Information About a Specific Timeout Rule by ID + zscaler.zpacloud.zpa_policy_timeout_rule_info: + id: "216196257331292020" +""" + +RETURN = """ +# Returns information on a specified policy timeout rule. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, + deleteNone, +) + + +def core(module): + policy_rule_name = module.params.get("name", None) + policy_rule_id = module.params.get("id", None) + client = ZPAClientHelper(module) + policy_rules = [] + if policy_rule_id is not None: + policy_rule = client.policies.get_rule( + policy_type="timeout", rule_id=policy_rule_id + ) + if policy_rule is None: + module.fail_json(msg="Failed to retrieve policy rule ID: '%s'" % (id)) + policy_rules = [policy_rule] + elif policy_rule_name is not None: + rules = client.policies.list_rules(policy_type="timeout").to_list() + found = False + for rule in rules: + if rule.get("name") == policy_rule_name: + policy_rules = [rule] + found = True + break + if not found: + module.fail_json( + msg="Failed to retrieve policy rule Name: '%s'" % (policy_rule_name) + ) + else: + policy_rules = client.policies.list_rules(policy_type="timeout").to_list() + module.exit_json(changed=False, data=policy_rules) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + name=dict(type="str", required=False), + id=dict(type="str", required=False), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_posture_profile_info.py b/plugins/modules/zpa_posture_profile_info.py new file mode 100644 index 0000000..1be2a54 --- /dev/null +++ b/plugins/modules/zpa_posture_profile_info.py @@ -0,0 +1,123 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_posture_profile_info +short_description: Retrieves details of a posture profile resource. +description: + - This module will allow the retrieval of information about a posture profile resource. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - Name of the posture profile. + required: false + type: str + id: + description: + - ID of the posture profile. + required: false + type: str +""" + +EXAMPLES = """ +- name: Get Information About All Posture Profiles + zscaler.zpacloud.zpa_posture_profile_info: + +- name: Get Details of a Specific Posture Profile by ID + zscaler.zpacloud.zpa_posture_profile_info: + id: "216196257331282583" +- name: Get Details of a Specific Posture Profile by Name + zscaler.zpacloud.zpa_posture_profile_info: + name: CrowdStrike_ZPA_Pre-ZTA +""" + +RETURN = """ +# Returns information on a specified posture profile. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, +) +import re + + +def remove_cloud_suffix(s: str) -> str: + reg = re.compile(r"(.*)[\s]+\([a-zA-Z0-9\-_\.]*\)[\s]*$") + res = reg.sub(r"\1", s) + return res.strip() + + +def core(module: AnsibleModule): + profile_id = module.params.get("id", None) + profile_name = module.params.get("name", None) + client = ZPAClientHelper(module) + profiles = [] + if profile_id is not None: + profile_box = client.posture_profiles.get_profile(profile_id=profile_id) + if profile_box is None: + module.fail_json( + msg="Failed to retrieve Posture Profile ID: '%s'" % (profile_id) + ) + profiles = [profile_box.to_dict()] + else: + profiles = client.posture_profiles.list_profiles().to_list() + if profile_name is not None: + profile_found = False + for profile in profiles: + if remove_cloud_suffix(profile.get("name")) == remove_cloud_suffix( + profile_name + ): + profile_found = True + profiles = [profile] + if not profile_found: + module.fail_json( + msg="Failed to retrieve Posture Profile Name: '%s'" + % (profile_name) + ) + module.exit_json(changed=False, data=profiles) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + name=dict(type="str", required=False), + id=dict(type="str", required=False), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_provisioning_key.py b/plugins/modules/zpa_provisioning_key.py new file mode 100644 index 0000000..a433f8f --- /dev/null +++ b/plugins/modules/zpa_provisioning_key.py @@ -0,0 +1,220 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_provisioning_key +short_description: Create a Provisioning Key. +description: + - This module will create/update/delete a specific Provisioning Key by association type (CONNECTOR_GRP or SERVICE_EDGE_GRP). +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + enabled: + description: "" + type: bool + default: True + required: False + id: + description: "" + type: str + required: False + max_usage: + description: "" + type: str + required: True + name: + description: "" + type: str + required: True + provisioning_key: + description: "" + type: str + required: False + enrollment_cert_id: + description: "" + type: str + required: True + usage_count: + description: "" + type: str + required: False + zcomponent_id: + description: "" + type: str + required: True + association_type: + description: "" + type: str + choices: ['connector', 'service_edge'] + required: True + state: + description: "" + type: str + choices: ['present', 'absent'] + default: present +""" + +EXAMPLES = """ +- name: Get ID Information of a Connector Enrollment Certificate + zscaler.zpacloud.zpa_enrollement_certificate_info: + name: "Connector" + register: enrollment_cert_connector + +- name: Get ID Information of a App Connector Group + zscaler.zpacloud.zpa_app_connector_groups_info: + name: "Example" + register: app_connector_group + +- name: "Create/Update/Delete App Connector Group Provisioning Key" + zscaler.zpacloud.zpa_provisioning_key: + name: "App Connector Group Provisioning Key" + association_type: "connector" + max_usage: "10" + enrollment_cert_id: "{{ enrollment_cert_connector.data[0].id }}" + zcomponent_id: "{{ enrollment_cert_connector.data[0].id }}" +""" + +RETURN = """ +# The newly created app connector group or service edge group provisioning key resource record. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, + deleteNone, +) + + +def core(module): + state = module.params.get("state", None) + client = ZPAClientHelper(module) + provisioning_key = dict() + params = [ + "enabled", + "id", + "max_usage", + "name", + "provisioning_key", + "enrollment_cert_id", + "usage_count", + "zcomponent_id", + "association_type", + ] + for param_name in params: + provisioning_key[param_name] = module.params.get(param_name, None) + provisioning_key_id = module.params.get("id", None) + provisioning_key_name = module.params.get("name", None) + association_type = module.params.get("association_type") + existing_key = None + if provisioning_key_id is not None: + existing_key = client.provisioning.get_provisioning_key( + key_id=provisioning_key_id, key_type=association_type + ) + elif provisioning_key_name is not None: + keys = client.provisioning.list_provisioning_keys( + key_type=association_type + ).to_list() + for k in keys: + if k.get("name") == provisioning_key_name: + existing_key = k + break + if existing_key is not None: + id = existing_key.get("id") + existing_key.update(provisioning_key) + existing_key["id"] = id + if state == "present": + if existing_key is not None: + """Update""" + existing_key = deleteNone( + dict( + key_id=existing_key.get("id", None), + key_type=association_type, + name=existing_key.get("name", None), + max_usage=existing_key.get("max_usage", None), + enrollment_cert_id=existing_key.get("enrollment_cert_id", None), + component_id=existing_key.get("zcomponent_id", None), + ) + ) + existing_key = client.provisioning.update_provisioning_key(**existing_key) + module.exit_json(changed=True, data=existing_key) + else: + """Create""" + provisioning_key = deleteNone( + dict( + key_type=association_type, + name=provisioning_key.get("name", None), + max_usage=provisioning_key.get("max_usage", None), + enrollment_cert_id=provisioning_key.get("enrollment_cert_id", None), + component_id=provisioning_key.get("zcomponent_id", None), + enabled=provisioning_key.get("enabled", None), + ) + ) + provisioning_key = client.provisioning.add_provisioning_key( + **provisioning_key + ) + module.exit_json(changed=False, data=provisioning_key) + elif state == "absent" and existing_key is not None: + client.provisioning.delete_provisioning_key( + key_id=existing_key.get("id"), key_type=association_type + ) + module.exit_json(changed=False, data=existing_key) + module.exit_json(changed=False, data={}) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + enabled=dict(type="bool", default=True, required=False), + id=dict(type="str", required=False), + max_usage=dict(type="str", required=True), + name=dict(type="str", required=True), + provisioning_key=dict( + type="str", + required=False, + no_log=True, + ), + enrollment_cert_id=dict(type="str", required=True), + usage_count=dict(type="str", required=False), + zcomponent_id=dict(type="str", required=True), + association_type=dict( + type="str", + choices=["CONNECTOR_GRP", "connector", "service_edge", "SERVICE_EDGE_GRP"], + required=True, + ), + state=dict(type="str", choices=["present", "absent"], default="present"), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_provisioning_key_info.py b/plugins/modules/zpa_provisioning_key_info.py new file mode 100644 index 0000000..8d4817b --- /dev/null +++ b/plugins/modules/zpa_provisioning_key_info.py @@ -0,0 +1,139 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_provisioning_key_info +short_description: Retrieves details about a Provisioning Key. +description: + - This module will allow the retrieval of information abouta Provisioning Key by association type (CONNECTOR_GRP or SERVICE_EDGE_GRP). +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - Name of the provisioning key. + required: false + type: str + id: + description: + - ID of the provisioning key. + required: false + type: str + association_type: + type: str + required: true + choices: ["CONNECTOR_GRP", "SERVICE_EDGE_GRP"] + description: + - "Specifies the provisioning key type for App Connectors or ZPA Private Service Edges." + - "The supported values are CONNECTOR_GRP and SERVICE_EDGE_GRP." +""" + +EXAMPLES = """ +- name: Gather Details of All SERVICE_EDGE_GRP Provisioning Keys + zscaler.zpacloud.zpa_provisioning_key_info: + association_type: "SERVICE_EDGE_GRP" + +- name: Gather Details of All SERVICE_EDGE_GRP Provisioning Keys by Name + zscaler.zpacloud.zpa_provisioning_key_info: + name: "Example Service Edge Group" + association_type: "SERVICE_EDGE_GRP" + +- name: Gather Details of All SERVICE_EDGE_GRP Provisioning Keys by ID + zscaler.zpacloud.zpa_provisioning_key_info: + id: "8691" + association_type: "SERVICE_EDGE_GRP" +""" + +RETURN = """ +# Returns information on a specified provisioning key resource. +""" + +from re import T +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, +) + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule + + +def core(module: AnsibleModule): + provisioning_key_id = module.params.get("id", None) + provisioning_key_name = module.params.get("name", None) + association_type = module.params.get("association_type", None) + client = ZPAClientHelper(module) + keys = [] + if provisioning_key_id is not None: + key_box = client.provisioning.get_provisioning_key( + key_id=provisioning_key_id, key_type=association_type + ) + if key_box is None: + module.fail_json( + msg="Failed to retrieve App Connector ID: '%s'" % (provisioning_key_id) + ) + keys = [key_box.to_dict()] + else: + keys = client.provisioning.list_provisioning_keys( + key_type=association_type + ).to_list() + if provisioning_key_name is not None: + key_found = False + for key in keys: + if key.get("name") == provisioning_key_name: + key_found = True + keys = [key] + if not key_found: + module.fail_json( + msg="Failed to retrieve App Connector Name: '%s'" + % (provisioning_key_name) + ) + module.exit_json(changed=False, data=keys) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + name=dict(type="str", required=False), + id=dict(type="str", required=False), + association_type=dict( + type="str", choices=["connector", "service_edge"], required=True + ), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_saml_attribute_info.py b/plugins/modules/zpa_saml_attribute_info.py new file mode 100644 index 0000000..fa823b5 --- /dev/null +++ b/plugins/modules/zpa_saml_attribute_info.py @@ -0,0 +1,135 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +from http import client +from multiprocessing.connection import Client + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_saml_attribute_info +short_description: Retrieves saml attributes from a given IDP +description: + - This module will allow the retrieval of information about a saml attributes from a given IDP +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +requirements: + - supported starting from zpa_api >= 1.0 +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - Name of the saml attribute. + required: false + type: str + id: + description: + - ID of the saml attribute. + required: false + type: str + idp_name: + description: + - Name of the IDP. + required: false + type: str +""" + +EXAMPLES = """ +- name: Get Information About All SAML Attributes + zscaler.zpacloud.zpa_saml_attribute_info: +- name: Get Information About Saml Attribute by Attribute Name + zscaler.zpacloud.zpa_saml_attribute_info: + name: DepartmentName_User +- name: Get Information About Saml Attribute by Attribute ID + zscaler.zpacloud.zpa_saml_attribute_info: + id: 216196257331285827 +""" + +RETURN = """ +# Returns information on a specified SAML attribute. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, +) + + +def core(module: AnsibleModule): + saml_attr_name = module.params.get("name", None) + saml_attr_id = module.params.get("id", None) + idp_name = module.params.get("idp_name", None) + client = ZPAClientHelper(module) + saml_attributes = [] + if saml_attr_id is not None: + attribute_box = client.saml_attributes.get_attribute(attribute_id=saml_attr_id) + if attribute_box is None: + module.fail_json(msg="Failed to retrieve saml attribute ID: '%s'" % (id)) + saml_attributes = [attribute_box.to_dict()] + elif saml_attr_name is not None: + attributes = client.saml_attributes.list_attributes().to_list() + if attributes is None: + module.fail_json( + msg="Failed to retrieve saml attribute Name: '%s'" % (saml_attr_name) + ) + saml_attr_found = False + for saml_attribute in attributes: + if saml_attribute.get("name") == saml_attr_name: + saml_attr_found = True + saml_attributes = [saml_attribute] + if not saml_attr_found: + module.fail_json( + msg="Failed to retrieve SAML attribute Name: '%s'" % (saml_attr_name) + ) + elif idp_name is not None: + idp_id = "" + idps = client.idp.list_idps() + for idp in idps: + if idp.get("name") == idp_name: + idp_id = idp.get("id") + saml_attributes = client.saml_attributes.list_attributes_by_idp( + idp_id=idp_id + ).to_list() + else: + saml_attributes = client.saml_attributes.list_attributes().to_list() + module.exit_json(changed=False, data=saml_attributes) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + name=dict(type="str", required=False), + idp_name=dict(type="str", required=False), + id=dict(type="str", required=False), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_scim_attribute_header_info.py b/plugins/modules/zpa_scim_attribute_header_info.py new file mode 100644 index 0000000..5b928ce --- /dev/null +++ b/plugins/modules/zpa_scim_attribute_header_info.py @@ -0,0 +1,139 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_scim_attribute_header_info +short_description: Retrieves scim attribute header from a given IDP +description: + - This module will allow the retrieval of information about scim attribute header from a given IDP +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - Name of the scim attribute. + required: false + type: str + idp_name: + description: + - Name of the IDP, required when ID is not sepcified. + required: true + type: str + id: + description: + - ID of the scim attribute. + required: false + type: str +""" + +EXAMPLES = """ +- name: Get Information About All SCIM Attribute of an IDP + zscaler.zpacloud.zpa_scim_attribute_header_info: + idp_name: IdP_Name +- name: Get Information About the SCIM Attribute by Name + zscaler.zpacloud.zpa_scim_attribute_header_info: + name: costCenter + idp_name: IdP_Name +- name: Get Information About the SCIM Attribute by ID + zscaler.zpacloud.zpa_scim_attribute_header_info: + id: 216196257331285842 + idp_name: IdP_Name +""" + +RETURN = """ +# Returns information on a specified SCIM Attribute Header. +""" + +from re import T +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, +) + + +def core(module): + scim_attr_name = module.params.get("name", None) + idp_name = module.params.get("idp_name", None) + scim_attr_id = module.params.get("id", None) + client = ZPAClientHelper(module) + idp_id = "" + idps = client.idp.list_idps() + for idp in idps: + if idp.get("name") == idp_name: + idp_id = idp.get("id") + if idp_id == "": + module.fail_json(msg="Failed to retrieve IDP with name : '%s'" % (idp_name)) + attributes = [] + if scim_attr_id is not None: + attribute_box = client.scim_attributes.get_attribute( + idp_id=idp_id, attribute_id=scim_attr_id + ) + if attribute_box is None: + module.fail_json( + msg="Failed to retrieve scim attribute header ID: '%s'" % (id) + ) + attributes = [attribute_box.to_dict()] + elif scim_attr_name is not None: + scim_attribute_found = False + attributes_list = client.scim_attributes.list_attributes_by_idp( + idp_id=idp_id + ).to_list() + for attribute in attributes_list: + if attribute.get("name") == scim_attr_name: + scim_attribute_found = True + attributes = [attribute] + break + if not scim_attribute_found: + module.fail_json( + msg="Failed to retrieve scim attribute header Name: '%s'" + % (scim_attr_name) + ) + else: + attributes = client.scim_attributes.list_attributes_by_idp( + idp_id=idp_id + ).to_list() + module.exit_json(changed=False, data=attributes) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + name=dict(type="str", required=False), + id=dict(type="str", required=False), + idp_name=dict(type="str", required=True), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_scim_group_info.py b/plugins/modules/zpa_scim_group_info.py new file mode 100644 index 0000000..55a4c9a --- /dev/null +++ b/plugins/modules/zpa_scim_group_info.py @@ -0,0 +1,126 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_scim_group_info +short_description: Retrieves scim group information from a given IDP +description: + - This module will allow the retrieval of information about scim group(s) from a given IDP +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - Name of the scim group. + required: false + type: str + idp_name: + description: + - Name of the IDP. + required: true + type: str + id: + description: + - ID of the scim group. + required: false + type: str +""" + +EXAMPLES = """ +- name: Get Information About All SCIM Groups from an IdP + zscaler.zpacloud.zpa_scim_attribute_header_info: + idp_name: "IdP_Name" +- name: Get Information About a SCIM Group by ID + zscaler.zpacloud.zpa_scim_attribute_header_info: + id: 216196257331285827 + idp_name: "IdP_Name" +- name: Get Information About a SCIM Group by Name + zscaler.zpacloud.zpa_scim_attribute_header_info: + name: "Finance" + idp_name: "IdP_Name" +""" + +RETURN = """ +# Returns information on a specified posture profile. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, +) + + +def core(module): + scim_group_name = module.params.get("name", None) + scim_group_id = module.params.get("id", None) + idp_name = module.params.get("idp_name", None) + client = ZPAClientHelper(module) + scim_groups = [] + if scim_group_id is not None: + attribute_box = client.scim_groups.get_group(group_id=scim_group_id) + if attribute_box is None: + module.fail_json(msg="Failed to retrieve scim group ID: '%s'" % (id)) + scim_groups = [attribute_box.to_dict()] + else: + idp_id = "" + idps = client.idp.list_idps() + for idp in idps: + if idp.get("name") == idp_name: + idp_id = idp.get("id") + scim_groups = client.scim_groups.list_groups(idp_id=idp_id).to_list() + if scim_group_name is not None: + scim_group_found = False + for scim_group in scim_groups: + if scim_group.get("name") == scim_group_name: + scim_group_found = True + scim_groups = [scim_group] + if not scim_group_found: + module.fail_json( + msg="Failed to retrieve App Connector Group Name: '%s'" + % (scim_group_name) + ) + module.exit_json(changed=False, data=scim_groups) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + name=dict(type="str", required=False), + id=dict(type="str", required=False), + idp_name=dict(type="str", required=True), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_segment_group.py b/plugins/modules/zpa_segment_group.py new file mode 100644 index 0000000..6a2e0b9 --- /dev/null +++ b/plugins/modules/zpa_segment_group.py @@ -0,0 +1,203 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_segment_group +short_description: Create a Segment Group +description: + - This module will create/update/delete a segment group resource. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + application_ids: + description: "" + type: list + required: False + elements: str + config_space: + description: "" + type: str + required: False + default: DEFAULT + choices: ['DEFAULT', 'SIEM'] + description: + description: "" + type: str + required: False + enabled: + description: "" + type: bool + required: False + id: + description: "" + type: str + name: + description: "" + type: str + required: True + policy_migrated: + description: "" + type: bool + required: False + tcp_keep_alive_enabled: + description: "" + type: str + required: False + state: + description: "" + type: str + choices: ['present', 'absent'] + default: present +""" + +EXAMPLES = """ +- name: Create/Update/Delete a Segment Group + zscaler.zpacloud.zpa_segment_group: + name: Example Segment Group + config_space: "DEFAULT" + description: Example Segment Group + enabled: true + policy_migrated: true + tcp_keep_alive_enabled: "1" +""" + +RETURN = """ +# The newly created segment group resource record. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + deleteNone, + ZPAClientHelper, +) + + +def core(module): + state = module.params.get("state", None) + client = ZPAClientHelper(module) + group = dict() + params = [ + "id", + "application_ids", + "config_space", + "description", + "enabled", + "name", + "policy_migrated", + "tcp_keep_alive_enabled", + ] + for param_name in params: + group[param_name] = module.params.get(param_name, None) + group_id = group.get("id", None) + group_name = group.get("name", None) + existing_group = None + if group_id is not None: + group_box = client.segment_groups.get_group(group_id=group_id) + if group_box is not None: + existing_group = group_box.to_dict() + elif group_name is not None: + groups = client.segment_groups.list_groups().to_list() + for group_ in groups: + if group_.get("name") == group_name: + existing_group = group_ + if existing_group is not None: + id = existing_group.get("id") + existing_group.update(group) + existing_group["id"] = id + if state == "present": + if existing_group is not None: + """Update""" + existing_group = deleteNone( + dict( + group_id=existing_group.get("id"), + name=existing_group.get("name"), + description=existing_group.get("description"), + enabled=existing_group.get("enabled"), + config_space=existing_group.get("config_space"), + policy_migrated=existing_group.get("policy_migrated"), + tcp_keep_alive_enabled=existing_group.get("tcp_keep_alive_enabled"), + application_ids=group.get("application_ids"), + ) + ) + existing_group = client.segment_groups.update_group( + **existing_group + ).to_dict() + module.exit_json(changed=True, data=existing_group) + else: + """Create""" + group = deleteNone( + dict( + name=group.get("name"), + description=group.get("description"), + enable=group.get("enable"), + config_space=group.get("config_space"), + policy_migrated=group.get("policy_migrated"), + tcp_keep_alive_enabled=group.get("tcp_keep_alive_enabled"), + application_ids=group.get("application_ids"), + ) + ) + group = client.segment_groups.add_group(**group).to_dict() + module.exit_json(changed=True, data=group) + elif state == "absent": + if existing_group is not None: + code = client.segment_groups.delete_group(group_id=existing_group.get("id")) + if code > 299: + module.exit_json(changed=False, data=None) + module.exit_json(changed=True, data=existing_group) + module.exit_json(changed=False, data={}) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + application_ids=dict( + type="list", + elements="str", + required=False, + ), + config_space=dict( + type="str", required=False, default="DEFAULT", choices=["DEFAULT", "SIEM"] + ), + description=dict(type="str", required=False), + enabled=dict(type="bool", required=False), + id=dict(type="str"), + name=dict(type="str", required=True), + policy_migrated=dict(type="bool", required=False), + tcp_keep_alive_enabled=dict(type="str", required=False), + state=dict(type="str", choices=["present", "absent"], default="present"), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_segment_group_info.py b/plugins/modules/zpa_segment_group_info.py new file mode 100644 index 0000000..89f139e --- /dev/null +++ b/plugins/modules/zpa_segment_group_info.py @@ -0,0 +1,114 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_segment_group_info +short_description: Retrieves information about a segment group. +description: + - This module will allow the retrieval of information about a segment group. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - Name of the server group. + required: false + type: str + id: + description: + - ID of the server group. + required: false + type: str +""" + +EXAMPLES = """ +- name: Get Detail Information of All Segment Groups + zscaler.zpacloud.zpa_segment_group_info: + +- name: Get Details of a Segment Group by Name + zscaler.zpacloud.zpa_segment_group_info: + name: "Example" + +- name: Get Details of a Segment Group by ID + zscaler.zpacloud.zpa_segment_group_info: + id: "216196257331291969" +""" + +RETURN = """ +# Returns information on a specified segment group. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, +) + + +def core(module: AnsibleModule): + group_id = module.params.get("id", None) + group_name = module.params.get("name", None) + client = ZPAClientHelper(module) + groups = [] + if group_id is not None: + group_box = client.segment_groups.get_group(group_id=group_id) + if group_box is None: + module.fail_json( + msg="Failed to retrieve Segment Group ID: '%s'" % (group_id) + ) + groups = [group_box.to_dict()] + else: + groups = client.segment_groups.list_groups().to_list() + if group_name is not None: + group_found = False + for group in groups: + if group.get("name") == group_name: + group_found = True + groups = [group] + if not group_found: + module.fail_json( + msg="Failed to retrieve Segment Group Name: '%s'" % (group_name) + ) + module.exit_json(changed=False, data=groups) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + name=dict(type="str", required=False), + id=dict(type="str", required=False), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_server_group.py b/plugins/modules/zpa_server_group.py new file mode 100644 index 0000000..c81ea6e --- /dev/null +++ b/plugins/modules/zpa_server_group.py @@ -0,0 +1,243 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_server_group +short_description: Create a Server Group . +description: + - This module create/update/delete a Server Group resource in the ZPA Cloud. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + applications: + type: list + elements: dict + suboptions: + name: + required: false + type: str + description: "" + id: + required: true + type: str + description: "" + required: false + description: + - This field is a json array of server_group-connector-id objects only. + enabled: + type: bool + required: false + description: + - This field defines if the server group is enabled or disabled. + dynamic_discovery: + type: bool + required: false + description: + - This field controls dynamic discovery of the servers. + name: + type: str + required: True + description: + - This field defines the name of the server group. + server_ids: + type: list + elements: str + required: false + description: + - This field is a list of servers objects that are applicable only when dynamic discovery is disabled. + - Server name is required only in cases where the new servers need to be created in this API. For existing servers, pass only the serverId. + app_connector_group_ids: + type: list + elements: str + required: false + description: + - List of server_group-connector ID objects. + config_space: + description: + - config space. + type: str + required: false + choices: + - DEFAULT + - SIEM + default: DEFAULT + description: + type: str + required: False + description: + - This field is the description of the server group. + id: + type: str + description: "" + ip_anchored: + type: bool + required: False + description: "" + state: + description: + - Whether the server group should be present or absent. + default: present + choices: + - present + - absent + type: str + +""" + +EXAMPLES = """ +- name: Create/Update/Delete a Server Group - Dynamic Discovery Off + zscaler.zpacloud.zpa_server_group: + name: "Example" + description: "Example" + enabled: false + dynamic_discovery: false + app_connector_group_ids: + - id: "216196257331291921" + server_ids: + - id: "216196257331291921" + application_ids: + - id: "216196257331291921" +""" + +RETURN = """ +# The newly created server group resource record. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, + deleteNone, +) + + +def core(module): + state = module.params.get("state", None) + client = ZPAClientHelper(module) + server_group = dict() + params = [ + "id", + "ip_anchored", + "name", + "config_space", + "enabled", + "description", + "dynamic_discovery", + "server_ids", + "application_ids", + "app_connector_group_ids", + ] + for param_name in params: + server_group[param_name] = module.params.get(param_name, None) + group_id = server_group.get("id", None) + group_name = server_group.get("name", None) + existing_server_group = None + if group_id is not None: + group_box = client.server_groups.get_group(group_id=group_id) + if group_box is not None: + existing_server_group = group_box.to_dict() + elif group_name is not None: + groups = client.server_groups.list_groups().to_list() + for group_ in groups: + if group_.get("name") == group_name: + existing_server_group = group_ + if existing_server_group is not None: + id = existing_server_group.get("id") + existing_server_group.update(server_group) + existing_server_group["id"] = id + if state == "present": + if existing_server_group is not None: + """Update""" + existing_server_group = deleteNone( + dict( + group_id=existing_server_group.get("id"), + app_connector_group_ids=existing_server_group.get( + "app_connector_group_ids" + ), + application_ids=existing_server_group.get("application_ids"), + config_space=existing_server_group.get("config_space"), + description=existing_server_group.get("description"), + enabled=existing_server_group.get("enabled"), + ip_anchored=existing_server_group.get("ip_anchored"), + dynamic_discovery=existing_server_group.get("dynamic_discovery"), + server_ids=existing_server_group.get("server_ids"), + ) + ) + server_group = client.server_groups.update_group(**existing_server_group) + module.exit_json(changed=True, data=server_group) + else: + """Create""" + server_group = deleteNone( + dict( + name=server_group.get("name"), + app_connector_group_ids=server_group.get("app_connector_group_ids"), + application_ids=server_group.get("application_ids"), + config_space=server_group.get("config_space"), + description=server_group.get("description"), + enabled=server_group.get("enabled"), + ip_anchored=server_group.get("ip_anchored"), + dynamic_discovery=server_group.get("dynamic_discovery"), + server_ids=server_group.get("server_ids"), + ) + ) + server_group = client.server_groups.add_group(**server_group).to_dict() + module.exit_json(changed=False, data=server_group) + elif state == "absent" and existing_server_group is not None: + code = client.server_groups.delete_group(existing_server_group.get("id")) + if code > 299: + module.exit_json(changed=False, data=None) + module.exit_json(changed=True, data=existing_server_group) + module.exit_json(changed=False, data={}) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + id=dict(type="str"), + ip_anchored=dict(type="bool", required=False), + name=dict(type="str", required=True), + config_space=dict( + type="str", required=False, default="DEFAULT", choices=["DEFAULT", "SIEM"] + ), + enabled=dict(type="bool", required=False), + description=dict(type="str", required=False), + dynamic_discovery=dict(type="bool", required=False), + server_ids=dict(type="list", elements="str", required=False), + application_ids=dict(type="list", elements="str", required=False), + app_connector_group_ids=dict(type="list", elements="str", required=False), + state=dict(type="str", choices=["present", "absent"], default="present"), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_server_group_info.py b/plugins/modules/zpa_server_group_info.py new file mode 100644 index 0000000..dffd51c --- /dev/null +++ b/plugins/modules/zpa_server_group_info.py @@ -0,0 +1,114 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_server_group_info +short_description: Retrieves information about an server group +description: + - This module will allow the retrieval of information about a server group resource. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - Name of the server group. + required: false + type: str + id: + description: + - ID of the server group. + required: false + type: str +""" + +EXAMPLES = """ +- name: Get Details of All Server Groups + zscaler.zpacloud.zpa_server_group_info: + +- name: Get Details of a Specific Server Group by Name + zscaler.zpacloud.zpa_server_group_info: + name: Example + +- name: Get Details of a Specific Server Group by ID + zscaler.zpacloud.zpa_server_group_info: + id: "216196257331291969" +""" + +RETURN = """ +# Returns information on a specified Server Group. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, +) + + +def core(module: AnsibleModule): + group_id = module.params.get("id", None) + group_name = module.params.get("name", None) + client = ZPAClientHelper(module) + groups = [] + if group_id is not None: + group_box = client.server_groups.get_group(group_id=group_id) + if group_box is None: + module.fail_json( + msg="Failed to retrieve Server Group ID: '%s'" % (group_id) + ) + groups = [group_box.to_dict()] + else: + groups = client.server_groups.list_groups().to_list() + if group_name is not None: + group_found = False + for group in groups: + if group.get("name") == group_name: + group_found = True + groups = [group] + if not group_found: + module.fail_json( + msg="Failed to retrieve Server Group Name: '%s'" % (group_name) + ) + module.exit_json(changed=False, data=groups) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + name=dict(type="str", required=False), + id=dict(type="str", required=False), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_service_edge_groups.py b/plugins/modules/zpa_service_edge_groups.py new file mode 100644 index 0000000..6d61c0f --- /dev/null +++ b/plugins/modules/zpa_service_edge_groups.py @@ -0,0 +1,344 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_app_connector_groups +short_description: Create an App Connector Group in the ZPA Cloud. +description: + - This module creates/update/delete an App Connector Group in the ZPA Cloud. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - Name of the App Connector Group. + required: true + type: str + description: + description: "" + required: false + type: str + connectors: + description: "Connectors" + required: false + type: list + elements: dict + suboptions: + name: + description: "Name of the App Connector Group." + required: false + type: str + id: + description: "id of the App Connector Group." + required: false + type: str + id: + description: "ID of the App Connector Group." + required: false + type: str + city_country: + description: + - City Country of the App Connector Group. + type: str + country_code: + description: + - Country code of the App Connector Group. + type: str + dns_query_type: + description: + - Whether to enable IPv4 or IPv6, or both, for DNS resolution of all applications in the App Connector Group. + type: str + choices: + - IPV4_IPV6 + - IPV4 + - IPV6 + default: IPV4_IPV6 + enabled: + description: + - Whether this App Connector Group is enabled or not. + type: bool + default: true + latitude: + description: + - Latitude of the App Connector Group. Integer or decimal. With values in the range of -90 to 90. + required: false + type: str + location: + description: + - Location of the App Connector Group. + required: false + type: str + longitude: + description: + - Longitude of the App Connector Group. Integer or decimal. With values in the range of -180 to 180. + required: false + type: str + lss_app_connector_group: + description: + - LSS app connector group + required: false + type: str + upgrade_day: + description: + - App Connectors in this group will attempt to update to a newer version of the software during this specified day. + - List of valid days (i.e., Sunday, Monday). + default: SUNDAY + type: str + upgrade_time_in_secs: + description: + - App Connectors in this group will attempt to update to a newer version of the software during this specified time. + - Integer in seconds (i.e., -66600). The integer should be greater than or equal to 0 and less than 86400, in 15 minute intervals. + default: 66600 + type: str + override_version_profile: + description: + - App Connectors in this group will attempt to update to a newer version of the software during this specified time. + - Integer in seconds (i.e., -66600). The integer should be greater than or equal to 0 and less than 86400, in 15 minute intervals. + type: bool + default: false + version_profile_id: + description: + - ID of the version profile. To learn more, see Version Profile Use Cases. + - This value is required, if the value for overrideVersionProfile is set to true. + type: str + default: '0' + choices: + - 0 + - 1 + - 2 + version_profile_name: + description: + - Name of the version profile. + type: str + state: + description: + - Whether the app connector group should be present or absent. + type: str + choices: + - present + - absent + default: present +""" + +EXAMPLES = """ +- name: Create/Update/Delete an App Connector Group + zscaler.zpacloud.zpa_app_connector_groups: + name: "Example" + description: "Example2" + enabled: true + city_country: "California, US" + country_code: "US" + latitude: "37.3382082" + longitude: "-121.8863286" + location: "San Jose, CA, USA" + upgrade_day: "SUNDAY" + upgrade_time_in_secs: "66600" + override_version_profile: true + version_profile_id: "0" + dns_query_type: "IPV4" +""" + +RETURN = """ +# The newly created app connector group resource record. +""" + + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + deleteNone, + ZPAClientHelper, +) + + +def core(module): + state = module.params.get("state", None) + client = ZPAClientHelper(module) + group = dict() + params = [ + "id", + "name", + "description", + "enabled", + "city_country", + "country_code", + "latitude", + "longitude", + "location", + "upgrade_day", + "upgrade_time_in_secs", + "dns_query_type", + "override_version_profile", + "version_profile_id", + "version_profile_name", + "use_in_dr_mode", + "trusted_networks_ids", + ] + for param_name in params: + group[param_name] = module.params.get(param_name, None) + group_id = group.get("id", None) + group_name = group.get("name", None) + existing_group = None + if group_id is not None: + group_box = client.service_edges.get_service_edge_group(group_id=group_id) + if group_box is not None: + existing_group = group_box.to_dict() + elif group_name is not None: + groups = client.service_edges.list_service_edge_groups().to_list() + for group_ in groups: + if group_.get("name") == group_name: + existing_group = group_ + if existing_group is not None: + id = existing_group.get("id") + existing_group.update(group) + existing_group["id"] = id + if state == "present": + if existing_group is not None: + """Update""" + existing_group = deleteNone( + dict( + group_id=existing_group.get("id"), + name=existing_group.get("name"), + description=existing_group.get("description"), + enabled=existing_group.get("enabled"), + city_country=existing_group.get("city_country"), + country_code=existing_group.get("country_code"), + latitude=existing_group.get("latitude"), + longitude=existing_group.get("longitude"), + location=existing_group.get("location"), + upgrade_day=existing_group.get("upgrade_day"), + upgrade_time_in_secs=existing_group.get("upgrade_time_in_secs"), + dns_query_type=existing_group.get("dns_query_type"), + override_version_profile=existing_group.get( + "override_version_profile" + ), + version_profile_id=existing_group.get("version_profile_id"), + version_profile_name=existing_group.get("version_profile_name"), + use_in_dr_mode=existing_group.get("use_in_dr_mode"), + trusted_networks_ids=existing_group.get("trusted_networks_ids"), + ) + ) + existing_group = client.service_edges.update_service_edge_group( + **existing_group + ).to_dict() + module.exit_json(changed=True, data=existing_group) + else: + """Create""" + group = deleteNone( + dict( + name=group.get("name"), + description=group.get("description"), + address=group.get("address"), + enable=group.get("enable"), + latitude=group.get("latitude"), + location=group.get("location"), + longitude=group.get("longitude"), + city_country=group.get("city_country"), + country_code=group.get("country_code"), + upgrade_day=group.get("upgrade_day"), + upgrade_time_in_secs=group.get("upgrade_time_in_secs"), + dns_query_type=group.get("dns_query_type"), + override_version_profile=group.get("override_version_profile"), + version_profile_id=group.get("version_profile_id"), + version_profile_name=group.get("version_profile_name"), + use_in_dr_mode=group.get("use_in_dr_mode"), + trusted_networks_ids=group.get("trusted_networks_ids"), + ) + ) + group = client.service_edges.add_service_edge_group(**group).to_dict() + module.exit_json(changed=True, data=group) + elif state == "absent": + if existing_group is not None and existing_group.get("id") is not None: + code = client.service_edges.delete_service_edge_group( + group_id=existing_group.get("id") + ) + if code > 299: + module.exit_json(changed=False, data=None) + module.exit_json(changed=True, data=existing_group) + module.exit_json(changed=False, data={}) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + id_name_spec = dict( + type="list", + elements="dict", + options=dict( + id=dict(type="str", required=False), name=dict(type="str", required=False) + ), + required=False, + ) + argument_spec.update( + connectors=id_name_spec, + id=dict(type="str", required=False), + name=dict(type="str", required=True), + enabled=dict(type="bool", default=True, required=False), + city_country=dict(type="str", required=False), + country_code=dict(type="str", required=False), + description=dict(type="str", required=False), + dns_query_type=dict( + type="str", + choices=["IPV4_IPV6", "IPV4", "IPV6"], + required=False, + default="IPV4_IPV6", + ), + latitude=dict(type="str", required=True), + location=dict(type="str", required=False), + longitude=dict(type="str", required=True), + upgrade_day=dict( + type="str", + choices=[ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY", + "SATURDAY", + "SUNDAY", + ], + default="SUNDAY", + required=False, + ), + upgrade_time_in_secs=dict(type="str", default=66600, required=False), + override_version_profile=dict(type="bool", default=False, required=False), + version_profile_id=dict( + type="str", choices=["0", "1", "2"], default="0", required=False + ), + version_profile_name=dict(type="str", choices=["Default", "Previous Default", "New Release"], required=False), + use_in_dr_mode=dict(type="bool", default=False, required=False), + state=dict(type="str", choices=["present", "absent"], default="present"), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_service_edge_groups_info.py b/plugins/modules/zpa_service_edge_groups_info.py new file mode 100644 index 0000000..005d9ff --- /dev/null +++ b/plugins/modules/zpa_service_edge_groups_info.py @@ -0,0 +1,116 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_service_edge_groups_info +short_description: Retrieves information about a Service Edge Group. +description: + - This module will allow the retrieval of information about a Service Edge Group resource. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - Name of the Service Edge Group.. + required: false + type: str + id: + description: + - ID of the Service Edge Group.. + required: false + type: str + +""" + +EXAMPLES = """ +- name: Get information about all Service Edge Groups + zscaler.zpacloud.zpa_service_edge_groups_info: +- name: Get information about Service Edge Connector Group by ID + zscaler.zpacloud.zpa_service_edge_groups_info: + id: "198288282" + +- name: Get information about Service Edge Connector Group by Name + zscaler.zpacloud.zpa_service_edge_groups_info: + name: "Example" +""" + +RETURN = """ +# Returns information on a specified Service Edge Group. +""" + + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, +) + + +def core(module: AnsibleModule): + group_id = module.params.get("id", None) + group_name = module.params.get("name", None) + client = ZPAClientHelper(module) + groups = [] + if group_id is not None: + group_box = client.service_edges.get_service_edge_group(group_id=group_id) + if group_box is None: + module.fail_json( + msg="Failed to retrieve Service Edge Group ID: '%s'" % (group_id) + ) + groups = [group_box.to_dict()] + else: + groups = client.service_edges.list_service_edge_groups().to_list() + if group_name is not None: + group_found = False + for group in groups: + if group.get("name") == group_name: + group_found = True + groups = [group] + if not group_found: + module.fail_json( + msg="Failed to retrieve Service Edge Group Name: '%s'" + % (group_name) + ) + module.exit_json(changed=False, data=groups) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + name=dict(type="str", required=False), + id=dict(type="str", required=False), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/zpa_trusted_networks_info.py b/plugins/modules/zpa_trusted_networks_info.py new file mode 100644 index 0000000..eb99f9c --- /dev/null +++ b/plugins/modules/zpa_trusted_networks_info.py @@ -0,0 +1,122 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, William Guilherme +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: zpa_trusted_networks_info +short_description: Retrieves information about a Trusted Network. +description: + - This module will allow the retrieval of information about Trusted Network resource. +author: + - William Guilherme (@willguibr) +version_added: "1.0.0" +requirements: + - Zscaler SDK Python can be obtained from PyPI U(https://pypi.org/project/zscaler-sdk-python/) +options: + client_id: + description: "" + required: false + type: str + client_secret: + description: "" + required: false + type: str + customer_id: + description: "" + required: false + type: str + name: + description: + - Name of the trusted network. + required: false + type: str + id: + description: + - ID of the trusted network. + required: false + type: str + +""" + +EXAMPLES = """ +- name: Get Information About All Trusted Networks + zscaler.zpacloud.zpa_trusted_network_info: +- name: Get information about Trusted Networks by Name + zscaler.zpacloud.zpa_trusted_network_info: + name: Corp-Trusted-Networks +- name: Get information about Trusted Networks by ID + zscaler.zpacloud.zpa_trusted_network_info: + id: 216196257331282234 +""" + +RETURN = """ +# Returns information on a specified Trusted Network. +""" + +from traceback import format_exc + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_client import ( + ZPAClientHelper, +) +import re + + +def remove_cloud_suffix(s: str) -> str: + reg = re.compile(r"(.*)[\s]+\([a-zA-Z0-9\-_\.]*\)[\s]*$") + res = reg.sub(r"\1", s) + return res.strip() + + +def core(module: AnsibleModule): + network_id = module.params.get("id", None) + network_name = module.params.get("name", None) + client = ZPAClientHelper(module) + networks = [] + if network_id is not None: + network_box = client.trusted_networks.get_network(network_id=network_id) + if network_box is None: + module.fail_json( + msg="Failed to retrieve Trusted Network ID: '%s'" % (network_id) + ) + networks = [network_box.to_dict()] + else: + networks = client.trusted_networks.list_networks().to_list() + if network_name is not None: + network_found = False + for network in networks: + if remove_cloud_suffix(network.get("name")) == remove_cloud_suffix( + network_name + ): + network_found = True + networks = [network] + if not network_found: + module.fail_json( + msg="Failed to retrieve Trusted Network Name: '%s'" % (network_name) + ) + module.exit_json(changed=False, data=networks) + + +def main(): + argument_spec = ZPAClientHelper.zpa_argument_spec() + argument_spec.update( + name=dict(type="str", required=False), + id=dict(type="str", required=False), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + try: + core(module) + except Exception as e: + module.fail_json(msg=to_native(e), exception=format_exc()) + + +if __name__ == "__main__": + main() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..d885346 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,43 @@ +[tool.poetry] +name = "zpacloud-ansible" +version = "2.10.0" +description = "Ansible collection for Zscaler Private Access (ZPA)" +authors = ["Zscaler Technology Alliances "] +license = "MIT" +keywords = ["zpacloud", "zpacloud-ansible", "zpa"] +readme = "README.md" +homepage = "https://github.com/zscaler/zpacloud-ansible" +repository = "https://github.com/zscaler/zpacloud-ansible" + +[tool.poetry.dependencies] +python = "^3.8" +zscaler-sdk-python = "^1.0.0" +xmltodict = "^0.12.0" +aiohttp = "^3.8.4" +dpath = "^2.1.5" + +[tool.poetry.dev-dependencies] +black = "22.3.0" +pytest = "*" +coverage = "4.5.4" +pylint = "2.13.*" +pytest-xdist = "*" +pytest-mock = "*" +voluptuous = "*" +yamllint = "*" +sphinx = "*" +sphinx-rtd-theme = "*" +#ansible-doc-extractor = "*" +rstcheck = "*" +six = "*" +pycodestyle = "*" +#ansible-core = "~2" +# from requirements.txt +certifi = "2021.5.30" +chardet = "3.0.4" +idna = "2.8" +requests = "^2.22.0" + +[build-system] +requires = ["poetry>=0.12"] +build-backend = "poetry.masonry.api" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f8db021 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,131 @@ +arrow==1.2.3 ; python_version >= "3.8" and python_version < "4.0" \ + --hash=sha256:3934b30ca1b9f292376d9db15b19446088d12ec58629bc3f0da28fd55fb633a1 \ + --hash=sha256:5a49ab92e3b7b71d96cd6bfcc4df14efefc9dfa96ea19045815914a6ab6b1fe2 +certifi==2021.5.30 ; python_version >= "3.8" and python_version < "4.0" \ + --hash=sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee \ + --hash=sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8 +charset-normalizer==3.1.0 ; python_version >= "3.8" and python_version < "4.0" \ + --hash=sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6 \ + --hash=sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1 \ + --hash=sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e \ + --hash=sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373 \ + --hash=sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62 \ + --hash=sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230 \ + --hash=sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be \ + --hash=sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c \ + --hash=sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0 \ + --hash=sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448 \ + --hash=sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f \ + --hash=sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649 \ + --hash=sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d \ + --hash=sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0 \ + --hash=sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706 \ + --hash=sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a \ + --hash=sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59 \ + --hash=sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23 \ + --hash=sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5 \ + --hash=sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb \ + --hash=sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e \ + --hash=sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e \ + --hash=sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c \ + --hash=sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28 \ + --hash=sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d \ + --hash=sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41 \ + --hash=sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974 \ + --hash=sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce \ + --hash=sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f \ + --hash=sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1 \ + --hash=sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d \ + --hash=sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8 \ + --hash=sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017 \ + --hash=sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31 \ + --hash=sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7 \ + --hash=sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8 \ + --hash=sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e \ + --hash=sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14 \ + --hash=sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd \ + --hash=sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d \ + --hash=sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795 \ + --hash=sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b \ + --hash=sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b \ + --hash=sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b \ + --hash=sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203 \ + --hash=sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f \ + --hash=sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19 \ + --hash=sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1 \ + --hash=sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a \ + --hash=sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac \ + --hash=sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9 \ + --hash=sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0 \ + --hash=sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137 \ + --hash=sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f \ + --hash=sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6 \ + --hash=sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5 \ + --hash=sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909 \ + --hash=sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f \ + --hash=sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0 \ + --hash=sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324 \ + --hash=sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755 \ + --hash=sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb \ + --hash=sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854 \ + --hash=sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c \ + --hash=sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60 \ + --hash=sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84 \ + --hash=sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0 \ + --hash=sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b \ + --hash=sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1 \ + --hash=sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531 \ + --hash=sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1 \ + --hash=sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11 \ + --hash=sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326 \ + --hash=sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df \ + --hash=sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab +idna==2.8 ; python_version >= "3.8" and python_version < "4.0" \ + --hash=sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407 \ + --hash=sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c +python-box==7.0.0 ; python_version >= "3.8" and python_version < "4.0" \ + --hash=sha256:011875da351e884658e41f04191df3ba7b7fa74a43506988b03168fb6b597d39 \ + --hash=sha256:194d1106fdcfde9bbd47cd1cf2a3c743e6b75f8f66c200b7353fa0e51596bbaa \ + --hash=sha256:25b93b8710add337c2330e8aa03fec3b06db742a858e2b5c8f3de1c48ac57507 \ + --hash=sha256:352f29a4fe26f7a77ead771682670740a112ddc14aa6ccf4550a2f8651c1b6b2 \ + --hash=sha256:4c006d2284b7eef0ce9b7eea829bf3efb52ab7b3584df9170c444b3030624057 \ + --hash=sha256:727af276177b3c2c0e7e2acea2152269ec85d9fc012a4ffe8df962e2a2d57613 \ + --hash=sha256:78a060b2d2af9a40f0bffce5b588f92fa86e07a68f0f2fa588fdf79e36971cf6 \ + --hash=sha256:87e0ad81bf934ec832df9e51f9880bb3e9b0cd99fe49d5bff9516bbf8108368c \ + --hash=sha256:8a25afebde5ab15f3b7e1eb33b63a98b9ebae24d12a1bc2f34862d4fa36a4ea2 \ + --hash=sha256:8e30a32226da7c4a23919d92fc6d4d4b9bc36e77e0702ecdcd1802e422dabc5d \ + --hash=sha256:9330cf1ffd3631e1a40867847cb4ca4b746f1effea7b052c3a8c0046dbae86a1 \ + --hash=sha256:973059f5b72c73feaaff5f3b5a10a34f138d11f197bfa8ef27b4e597ed13f259 \ + --hash=sha256:a681b16b7c6b437340cf1df1834e2a7f835c07a7189371563dd7ac70510a6c09 \ + --hash=sha256:ace7ddd55590c78aacf7b287fc6d346220e46093d10b7acffdb4b66a9ed8ea5c \ + --hash=sha256:ae5cb0f58ea299f3ac6b2ef065eaf4cd6192a2f7d3a7a560aa6012777271a724 \ + --hash=sha256:bb8b9d00ef3fd5110276202dcfa60d719609c1c98f24a7dd6a1b19676fd512dd \ + --hash=sha256:bc91dfb5672a6d4e0c7ca2672990bc84096a31a15d6449b9a58fb2f05b196a20 \ + --hash=sha256:c05a12aac622a07b8a84be6c5171e24fe2a2e58e1c7870439fa3696a407caa8d \ + --hash=sha256:c78ae2652566b1ea3095ea581c1c305016aa9a1d7705e4ab84db7d68ab43c5ca \ + --hash=sha256:e76e5b6797851ed6863585f2fdc6d472c4ec3d68886e966acd33da212d5030f5 \ + --hash=sha256:e82a6ebb2d049ba8bd475d64290d5334a1fb2b49fc54ee34809a115b7042aac2 \ + --hash=sha256:ec34c6577934b9fe8be82047671b4561f3589cff45b036792797941220317ced \ + --hash=sha256:ed0948c06f1b5a3d959cb79ee6825f509a6979d5599ca7a786fe856e9515fa31 \ + --hash=sha256:ee15b02b5d8c034553dd6dcaa0b1f0e56a49ca9f5d95a7c5883e83c00b8bd05c \ + --hash=sha256:ee9a38381c13d886945fda7e332307454aeb4db1478f6a2ce9ed6a45cdc002d0 \ + --hash=sha256:f0c3716334228b939d49d5e1f0e1f2619dbe8a72a9d7c82f19fab41c53f407f7 \ + --hash=sha256:fea273a8e40bcaf535a7f1253457e77f940cc2c059766a13769de4d68ccbcf16 +python-dateutil==2.8.2 ; python_version >= "3.8" and python_version < "4.0" \ + --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \ + --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9 +requests==2.30.0 ; python_version >= "3.8" and python_version < "4.0" \ + --hash=sha256:10e94cc4f3121ee6da529d358cdaeaff2f1c409cd377dbc72b825852f2f7e294 \ + --hash=sha256:239d7d4458afcb28a692cdd298d87542235f4ca8d36d03a15bfc128a6559a2f4 +restfly==1.4.7 ; python_version >= "3.8" and python_version < "4.0" \ + --hash=sha256:0a8e07f45745b6a4dabdee29befadc3d9803662afd423ab5ad7c13d70b9ccee2 \ + --hash=sha256:b50cb15f49c6ae1afb41514e01e1f6d1cb45d621176ea2fb2fa1e7faa301f914 +six==1.16.0 ; python_version >= "3.8" and python_version < "4.0" \ + --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ + --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 +urllib3==2.0.2 ; python_version >= "3.8" and python_version < "4.0" \ + --hash=sha256:61717a1095d7e155cdb737ac7bb2f4324a858a1e2e6466f6d03ff630ca68d3cc \ + --hash=sha256:d055c2f9d38dc53c808f6fdc8eab7360b6fdbbde02340ed25cfbcd817c62469e +xmltodict==0.12.0 ; python_version >= "3.8" and python_version < "4.0" \ + --hash=sha256:50d8c638ed7ecb88d90561beedbf720c9b4e851a9fa6c47ebd64e99d166d8a21 \ + --hash=sha256:8bbcb45cc982f48b2ca8fe7e7827c5d792f217ecf1792626f808bf41c3b86051 diff --git a/tests/integration/ansible.cfg b/tests/integration/ansible.cfg new file mode 100644 index 0000000..211a94d --- /dev/null +++ b/tests/integration/ansible.cfg @@ -0,0 +1,2 @@ +[defaults] +collections_paths = ../../../../.. \ No newline at end of file diff --git a/tests/integration/group_vars/all.yml b/tests/integration/group_vars/all.yml new file mode 100644 index 0000000..bc35571 --- /dev/null +++ b/tests/integration/group_vars/all.yml @@ -0,0 +1,2 @@ +--- +ansible_python_interpreter: '{{ ansible_playbook_python }}' \ No newline at end of file diff --git a/tests/integration/integration_config.yml.template b/tests/integration/integration_config.yml.template new file mode 100644 index 0000000..c05e038 --- /dev/null +++ b/tests/integration/integration_config.yml.template @@ -0,0 +1,4 @@ +--- +client_id: ${ZPA_CLIENT_ID} +client_secret: ${ZPA_CLIENT_SECRET} +customer_id: ${ZPA_CUSTOMER_ID} diff --git a/tests/integration/targets/zpa_app_connector_controller_info/aliases b/tests/integration/targets/zpa_app_connector_controller_info/aliases new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_app_connector_controller_info/defaults/main.yml b/tests/integration/targets/zpa_app_connector_controller_info/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_app_connector_controller_info/tasks/test_zpa_app_connector_controller_info.yml b/tests/integration/targets/zpa_app_connector_controller_info/tasks/test_zpa_app_connector_controller_info.yml new file mode 100644 index 0000000..1baa65b --- /dev/null +++ b/tests/integration/targets/zpa_app_connector_controller_info/tasks/test_zpa_app_connector_controller_info.yml @@ -0,0 +1,19 @@ +--- +- block: + + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id, client_secret, customer_id are not defined + - client_id, client_secret, customer_id | length == 0 + + - name: Gather App Connector Controller Information + zscaler.zpacloud.zpa_app_connector_controller_info: + register: result + + - name: Verify App Connector Controller info fetched + ansible.builtin.assert: + that: + - not result.changed + - result.data is defined diff --git a/tests/integration/targets/zpa_app_connector_groups/defaults/main.yml b/tests/integration/targets/zpa_app_connector_groups/defaults/main.yml new file mode 100644 index 0000000..a38128c --- /dev/null +++ b/tests/integration/targets/zpa_app_connector_groups/defaults/main.yml @@ -0,0 +1,13 @@ +name: san_jose +description: san_jose +enabled: true +city_country: California, US +country_code: US +latitude: 37.3382082 +longitude: -121.8863286 +location: San Jose, CA, USA +upgrade_day: SUNDAY +upgrade_time_in_secs: 66600 +override_version_profile: true +version_profile_id: "1" +dns_query_type: "IPV4_IPV6" \ No newline at end of file diff --git a/tests/integration/targets/zpa_app_connector_groups/tasks/main.yml b/tests/integration/targets/zpa_app_connector_groups/tasks/main.yml new file mode 100644 index 0000000..29bcc10 --- /dev/null +++ b/tests/integration/targets/zpa_app_connector_groups/tasks/main.yml @@ -0,0 +1,151 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + + - name: Ensure App Connector Group is absent (leftover) + zscaler.zpacloud.zpa_app_connector_groups: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + state: absent + name: "{{ name }}" + ignore_errors: true # In case one was left from previous run + register: result + + - name: Ensure App Connector Group is absent + zscaler.zpacloud.zpa_app_connector_groups: + state: absent + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "{{ name }}" + register: result + + - name: Verify App Connector Group is absent + ansible.builtin.assert: + that: + - not result.changed + + # Create App Connector Groups (Present) + - name: Ensure App Connector Group is (Present) + zscaler.zpacloud.zpa_app_connector_groups: + state: present + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "{{ name }}" + description: "{{ description }}" + enabled: "{{ enabled }}" + city_country: "{{ city_country }}" + country_code: "{{ country_code }}" + latitude: "{{ latitude }}" + longitude: "{{ longitude }}" + location: "{{ location }}" + upgrade_day: "{{ upgrade_day }}" + upgrade_time_in_secs: "{{ upgrade_time_in_secs }}" + override_version_profile: "{{ override_version_profile }}" + version_profile_id: "{{ version_profile_id }}" + dns_query_type: "{{ dns_query_type }}" + register: result + + - name: Verify app connector group is present + ansible.builtin.assert: + that: + - not result.changed + - result.data is defined + - result.data.name is defined + - result.data.name == name + - result.data.enabled == true + + - name: Create the App Connector Group (again; idempotency check) + zscaler.zpacloud.zpa_app_connector_groups: + state: present + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "{{ name }}" + description: "{{ description }}" + enabled: "{{ enabled }}" + city_country: "{{ city_country }}" + country_code: "{{ country_code }}" + latitude: "{{ latitude }}" + longitude: "{{ longitude }}" + location: "{{ location }}" + upgrade_day: "{{ upgrade_day }}" + upgrade_time_in_secs: "{{ upgrade_time_in_secs }}" + override_version_profile: "{{ override_version_profile }}" + version_profile_id: "{{ version_profile_id }}" + dns_query_type: "{{ dns_query_type }}" + register: result + + - name: Verify App Connector Group is absent (from absent) + ansible.builtin.assert: + that: + - result.changed + + - name: Fetch all App Connector Groups + zscaler.zpacloud.zpa_app_connector_groups_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + register: result + + - name: Ensure all App Connector Groups found + ansible.builtin.assert: + that: + - not result.changed + - result.data[0] is defined + + - name: Fetch this App Connector Group + zscaler.zpacloud.zpa_app_connector_groups_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "{{ name }}" + register: result + + - name: Ensure this App Connector Group is found + ansible.builtin.assert: + that: + - not result.changed + - result.data is defined + - result.data[0].name is defined + - result.data[0].name == name + - result.data[0].enabled == true + + - name: Delete App Connector Group + zscaler.zpacloud.zpa_app_connector_groups: + state: absent + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "{{ name }}" + register: result + + - name: Verify App Connector Group is Deleted + ansible.builtin.assert: + that: + - result.changed + + - name: Delete App Connector Group (again; idempotency check) + zscaler.zpacloud.zpa_app_connector_groups: + state: absent + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "{{ name }}" + register: result + + - name: Verify App Connector Group is absent + ansible.builtin.assert: + that: + - not result.changed \ No newline at end of file diff --git a/tests/integration/targets/zpa_app_connector_groups_info/aliases b/tests/integration/targets/zpa_app_connector_groups_info/aliases new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_app_connector_groups_info/tasks/main.yml b/tests/integration/targets/zpa_app_connector_groups_info/tasks/main.yml new file mode 100644 index 0000000..d5d012f --- /dev/null +++ b/tests/integration/targets/zpa_app_connector_groups_info/tasks/main.yml @@ -0,0 +1,23 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + - name: Gather App Connector Group Information + zscaler.zpacloud.zpa_app_connector_groups_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + register: result + - name: Verify app connector group info fetched + ansible.builtin.assert: + that: + - not result.changed + - result.data is defined \ No newline at end of file diff --git a/tests/integration/targets/zpa_application_segment/defaults/main.yml b/tests/integration/targets/zpa_application_segment/defaults/main.yml new file mode 100644 index 0000000..be0492d --- /dev/null +++ b/tests/integration/targets/zpa_application_segment/defaults/main.yml @@ -0,0 +1,12 @@ +name: Dummy Application for integration test +description: Dummy Application for integration test +enabled: true +health_reporting: ON_ACCESS +bypass_type: NEVER +is_cname_enabled: true +tcp_port_range: + - from: "80" + to: "80" +domain_names: + - crm1.example.com + - crm2.example.com diff --git a/tests/integration/targets/zpa_application_segment/tasks/main.yml b/tests/integration/targets/zpa_application_segment/tasks/main.yml new file mode 100644 index 0000000..274a8bb --- /dev/null +++ b/tests/integration/targets/zpa_application_segment/tasks/main.yml @@ -0,0 +1,287 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + - name: Ensure Application Segment is absent (leftover) + zscaler.zpacloud.zpa_application_segment: + state: absent + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "{{ name }}" + description: "{{ description }}" + enabled: "{{ enabled }}" + health_reporting: "{{ health_reporting }}" + bypass_type: "{{ bypass_type }}" + is_cname_enabled: "{{ is_cname_enabled }}" + tcp_port_range: "{{ tcp_port_range }}" + domain_names: "{{ domain_names }}" + segment_group_id: "216196257331291896" + server_groups: + - id: "216196257331291969" + ignore_errors: true # In case one was left from previous run + register: result + - name: Ensure Application Segment is absent + zscaler.zpacloud.zpa_application_segment: + state: absent + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "{{ name }}" + description: "{{ description }}" + enabled: "{{ enabled }}" + health_reporting: "{{ health_reporting }}" + bypass_type: "{{ bypass_type }}" + is_cname_enabled: "{{ is_cname_enabled }}" + tcp_port_range: "{{ tcp_port_range }}" + domain_names: "{{ domain_names }}" + segment_group_id: "216196257331291896" + server_groups: + - id: "216196257331291969" + ignore_errors: true # In case one was left from previous run + register: result + - name: Verify Application Segment is absent + ansible.builtin.assert: + that: + - not result.changed + - name: Create a segment group + zscaler.zpacloud.zpa_segment_group: + state: present + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + config_space: "DEFAULT" + name: Dummy Application group for integration test + description: Dummy Application group for integration test + enabled: true + policy_migrated: true + tcp_keep_alive_enabled: "1" + register: segment_group + - name: Verify Application Segment group is created + ansible.builtin.assert: + that: + - segment_group.data + - segment_group.data.name is defined + - segment_group.data.id is defined + - segment_group.data.enabled is true + - name: Create the App Connector Group + zscaler.zpacloud.zpa_app_connector_groups: + state: present + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "Dummy Application connector group for integration test" + description: "Dummy Application connector group for integration test" + enabled: true + city_country: California, US + country_code: US + latitude: 37.3382082 + longitude: -121.8863286 + location: San Jose, CA, USA + upgrade_day: SUNDAY + upgrade_time_in_secs: 66600 + override_version_profile: true + version_profile_id: "1" + dns_query_type: "IPV4" + register: app_connector_group + - name: Verify Application connector group is created + ansible.builtin.assert: + that: + - app_connector_group.data + - app_connector_group.data.name is defined + - app_connector_group.data.id is defined + - app_connector_group.data.enabled is true + - name: Create a server group + zscaler.zpacloud.zpa_server_group: + state: present + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "Dummy server group for integration test" + description: "Dummy server group for integration test" + enabled: true + dynamic_discovery: true + app_connector_groups: + - id: "{{app_connector_group.data.id}}" + register: server_group + - name: Verify Server group is created + ansible.builtin.assert: + that: + - server_group.data + - server_group.data.name is defined + - server_group.data.id is defined + - server_group.data.enabled is true + # Create Application Segments (Present) + - name: Ensure Application Segment is (Present) + zscaler.zpacloud.zpa_application_segment: + state: present + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "{{ name }}" + description: "{{ description }}" + enabled: "{{ enabled }}" + health_reporting: "{{ health_reporting }}" + bypass_type: "{{ bypass_type }}" + is_cname_enabled: "{{ is_cname_enabled }}" + tcp_port_range: "{{ tcp_port_range }}" + domain_names: "{{ domain_names }}" + segment_group_id: "{{ segment_group.data.id }}" + server_groups: + - id: "{{ server_group.data.id }}" + register: result + - name: Verify Application Segment is present + ansible.builtin.assert: + that: + - result.data + - result.data.name is defined + - result.data.enabled is true + - name: Create the Application Segment (again; idempotency check) + zscaler.zpacloud.zpa_application_segment: + state: present + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "{{ name }}" + description: "{{ description }}" + enabled: "{{ enabled }}" + health_reporting: "{{ health_reporting }}" + bypass_type: "{{ bypass_type }}" + is_cname_enabled: "{{ is_cname_enabled }}" + tcp_port_range: "{{ tcp_port_range }}" + domain_names: "{{ domain_names }}" + segment_group_id: "{{ segment_group.data.id }}" + server_groups: + - id: "{{ server_group.data.id }}" + register: result + - name: Verify Application Segment is present (again; idempotency check) + ansible.builtin.assert: + that: + - result.changed + - name: Fetch all Application Segments + zscaler.zpacloud.zpa_application_segment_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + register: result + - name: Ensure all Application Segments found + ansible.builtin.assert: + that: + - not result.changed + - result.data[0] is defined + - name: Give the ZPA Cloud a 5 seconds to settle + ansible.builtin.pause: + seconds: 5 + - name: Delete Application Segment + zscaler.zpacloud.zpa_application_segment: + state: absent + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "{{ name }}" + description: "{{ description }}" + enabled: "{{ enabled }}" + health_reporting: "{{ health_reporting }}" + bypass_type: "{{ bypass_type }}" + is_cname_enabled: "{{ is_cname_enabled }}" + tcp_port_range: "{{ tcp_port_range }}" + domain_names: "{{ domain_names }}" + segment_group_id: "{{ segment_group.data.id }}" + server_groups: + - id: "{{ server_group.data.id }}" + register: result + - name: Verify Application Segment is absent + ansible.builtin.assert: + that: + - result.changed + - name: Delete Application Segment (again; idempotency check) + zscaler.zpacloud.zpa_application_segment: + state: absent + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "{{ name }}" + description: "{{ description }}" + enabled: "{{ enabled }}" + health_reporting: "{{ health_reporting }}" + bypass_type: "{{ bypass_type }}" + is_cname_enabled: "{{ is_cname_enabled }}" + tcp_port_range: "{{ tcp_port_range }}" + domain_names: "{{ domain_names }}" + segment_group_id: "{{ segment_group.data.id }}" + server_groups: + - id: "{{ server_group.data.id }}" + register: result + - name: Verify Application Segment is absent + ansible.builtin.assert: + that: + - not result.changed + - name: Delete Segment Group + zscaler.zpacloud.zpa_segment_group: + state: absent + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + config_space: "DEFAULT" + name: "{{ segment_group.data.name }}" + description: Dummy Application group for integration test + enabled: true + policy_migrated: true + tcp_keep_alive_enabled: "1" + id: "{{ segment_group.data.id }}" + register: result + - name: Verify Segment Group is absent + ansible.builtin.assert: + that: + - result.changed + - name: Delete Server Group + zscaler.zpacloud.zpa_server_group: + state: absent + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "Dummy server group for integration test" + description: "Dummy server group for integration test" + enabled: true + dynamic_discovery: true + id: "{{ server_group.data.id }}" + app_connector_groups: + - id: "{{app_connector_group.data.id}}" + register: result + - name: Verify Server Group is absent + ansible.builtin.assert: + that: + - result.changed + - name: Delete App connector Group + zscaler.zpacloud.zpa_app_connector_groups: + state: absent + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "{{app_connector_group.data.name}}" + id: "{{app_connector_group.data.id}}" + description: "Dummy Application connector group for integration test" + enabled: true + city_country: California, US + country_code: US + latitude: 37.3382082 + longitude: -121.8863286 + location: San Jose, CA, USA + upgrade_day: SUNDAY + upgrade_time_in_secs: 66600 + override_version_profile: true + version_profile_id: "1" + dns_query_type: "IPV4" + register: result + - name: Verify App connector Group is absent + ansible.builtin.assert: + that: + - result.changed diff --git a/tests/integration/targets/zpa_application_segment_browser_access/defaults/main.yml b/tests/integration/targets/zpa_application_segment_browser_access/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_application_segment_browser_access/tasks/main.yml b/tests/integration/targets/zpa_application_segment_browser_access/tasks/main.yml new file mode 100644 index 0000000..350dcca --- /dev/null +++ b/tests/integration/targets/zpa_application_segment_browser_access/tasks/main.yml @@ -0,0 +1,12 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 \ No newline at end of file diff --git a/tests/integration/targets/zpa_application_segment_info/aliases b/tests/integration/targets/zpa_application_segment_info/aliases new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_application_segment_info/defaults/main.yml b/tests/integration/targets/zpa_application_segment_info/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_application_segment_info/tasks/main.yml b/tests/integration/targets/zpa_application_segment_info/tasks/main.yml new file mode 100644 index 0000000..6c6164a --- /dev/null +++ b/tests/integration/targets/zpa_application_segment_info/tasks/main.yml @@ -0,0 +1,25 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + + - name: Gather Application Segment Information + zscaler.zpacloud.zpa_application_segment_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + register: result + + - name: Verify application segment info fetched + ansible.builtin.assert: + that: + - not result.changed + - result.data is defined diff --git a/tests/integration/targets/zpa_application_server/defaults/main.yml b/tests/integration/targets/zpa_application_server/defaults/main.yml new file mode 100644 index 0000000..d0f5078 --- /dev/null +++ b/tests/integration/targets/zpa_application_server/defaults/main.yml @@ -0,0 +1,5 @@ +name: Dummy Application for integration test +description: Dummy Application for integration test +enabled: true +address: "192.168.1.1" +config_space: DEFAULT \ No newline at end of file diff --git a/tests/integration/targets/zpa_application_server/tasks/main.yml b/tests/integration/targets/zpa_application_server/tasks/main.yml new file mode 100644 index 0000000..6ef76bb --- /dev/null +++ b/tests/integration/targets/zpa_application_server/tasks/main.yml @@ -0,0 +1,61 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + + - name: Create Application Server + zscaler.zpacloud.zpa_application_server: + state: present + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "{{ name }}" + description: "{{ description }}" + enabled: "{{ enabled }}" + address: "{{ address }}" + config_space: "{{ config_space }}" + register: result + + - name: Find the Application Server + zscaler.zpacloud.zpa_application_server_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "{{ name }}" + register: result + + - name: Verify application server info fetched + ansible.builtin.assert: + that: + - not result.changed + - result.data is defined + - result.data[0].name is defined + - result.data[0].name == name + - result.data[0].address is defined + - result.data[0].address == address + - result.data[0].enabled == true + + always: + + - name: Delete Application Server + zscaler.zpacloud.zpa_application_server: + state: absent + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "{{ name }}" + address: "{{ address }}" + register: result + + - name: Verify Application Server is absent + ansible.builtin.assert: + that: + - not result.changed \ No newline at end of file diff --git a/tests/integration/targets/zpa_ba_certificate_info/aliases b/tests/integration/targets/zpa_ba_certificate_info/aliases new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_ba_certificate_info/defaults/main.yml b/tests/integration/targets/zpa_ba_certificate_info/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_ba_certificate_info/tasks/main.yml b/tests/integration/targets/zpa_ba_certificate_info/tasks/main.yml new file mode 100644 index 0000000..b6a88cc --- /dev/null +++ b/tests/integration/targets/zpa_ba_certificate_info/tasks/main.yml @@ -0,0 +1,25 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + + - name: Fetch all Browser Certificates + zscaler.zpacloud.zpa_ba_certificate_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + register: result + + - name: Ensure all Browser Certificates found + ansible.builtin.assert: + that: + - not result.changed + - result.data is defined \ No newline at end of file diff --git a/tests/integration/targets/zpa_cloud_connector_group_info/aliases b/tests/integration/targets/zpa_cloud_connector_group_info/aliases new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_cloud_connector_group_info/defaults/main.yml b/tests/integration/targets/zpa_cloud_connector_group_info/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_cloud_connector_group_info/tasks/main.yml b/tests/integration/targets/zpa_cloud_connector_group_info/tasks/main.yml new file mode 100644 index 0000000..80fc755 --- /dev/null +++ b/tests/integration/targets/zpa_cloud_connector_group_info/tasks/main.yml @@ -0,0 +1,25 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + + - name: Fetch all Cloud Connector Groups + zscaler.zpacloud.zpa_cloud_connector_group_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + register: result + + - name: Ensure all cloud connector group found + ansible.builtin.assert: + that: + - not result.changed + - result.data[0] is defined \ No newline at end of file diff --git a/tests/integration/targets/zpa_customer_version_profile_info/aliases b/tests/integration/targets/zpa_customer_version_profile_info/aliases new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_customer_version_profile_info/defaults/main.yml b/tests/integration/targets/zpa_customer_version_profile_info/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_customer_version_profile_info/tasks/main.yml b/tests/integration/targets/zpa_customer_version_profile_info/tasks/main.yml new file mode 100644 index 0000000..8f68354 --- /dev/null +++ b/tests/integration/targets/zpa_customer_version_profile_info/tasks/main.yml @@ -0,0 +1,25 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + + - name: Fetch all Version Profile Information + zscaler.zpacloud.zpa_customer_version_profile_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + register: result + + - name: Ensure all customer version profile info fetched + ansible.builtin.assert: + that: + - not result.changed + - result.data is defined \ No newline at end of file diff --git a/tests/integration/targets/zpa_enrollement_certificate_info/aliases b/tests/integration/targets/zpa_enrollement_certificate_info/aliases new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_enrollement_certificate_info/defaults/main.yml b/tests/integration/targets/zpa_enrollement_certificate_info/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_enrollement_certificate_info/tasks/main.yml b/tests/integration/targets/zpa_enrollement_certificate_info/tasks/main.yml new file mode 100644 index 0000000..4f34616 --- /dev/null +++ b/tests/integration/targets/zpa_enrollement_certificate_info/tasks/main.yml @@ -0,0 +1,92 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + + - name: Fetch Isolation Client Enrollment Certificate Information + zscaler.zpacloud.zpa_enrollement_certificate_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "Isolation Client" + register: isolation + + - name: Ensure isolation certificate info fetched + ansible.builtin.assert: + that: + - not isolation.changed + - isolation.data is defined + - isolation.data[0].name is defined + - isolation.data[0].name == "Isolation Client" + + - name: Fetch Service Edge Enrollment Certificate Information + zscaler.zpacloud.zpa_enrollement_certificate_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "Service Edge" + register: service_edge + + - name: Ensure service_edge certificate info fetched + ansible.builtin.assert: + that: + - not service_edge.changed + - service_edge.data is defined + - service_edge.data[0].name is defined + - service_edge.data[0].name == "Service Edge" + + - name: Fetch Connector Enrollment Certificate Information + zscaler.zpacloud.zpa_enrollement_certificate_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "Connector" + register: connector + + - name: Ensure service_edge certificate info fetched + ansible.builtin.assert: + that: + - not connector.changed + - connector.data is defined + - connector.data[0].name is defined + - connector.data[0].name == "Connector" + + - name: Fetch Client Enrollment Certificate Information + zscaler.zpacloud.zpa_enrollement_certificate_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "Client" + register: client + + - name: Ensure Client certificate info fetched + ansible.builtin.assert: + that: + - not client.changed + - client.data is defined + - client.data[0].name is defined + - client.data[0].name == "Client" + + - name: Fetch Root Enrollment Certificate Information + zscaler.zpacloud.zpa_enrollement_certificate_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "Root" + register: root + + - name: Ensure Root certificate info fetched + ansible.builtin.assert: + that: + - not root.changed + - root.data is defined + - root.data[0].name is defined + - root.data[0].name == "Root" \ No newline at end of file diff --git a/tests/integration/targets/zpa_idp_controller_info/aliases b/tests/integration/targets/zpa_idp_controller_info/aliases new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_idp_controller_info/defaults/main.yml b/tests/integration/targets/zpa_idp_controller_info/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_idp_controller_info/tasks/main.yml b/tests/integration/targets/zpa_idp_controller_info/tasks/main.yml new file mode 100644 index 0000000..a5028c3 --- /dev/null +++ b/tests/integration/targets/zpa_idp_controller_info/tasks/main.yml @@ -0,0 +1,25 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + + - name: Gather Identity Provider Information + zscaler.zpacloud.zpa_idp_controller_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + register: result + + - name: Verify identity provider info fetched + ansible.builtin.assert: + that: + - not result.changed + - result.data is defined \ No newline at end of file diff --git a/tests/integration/targets/zpa_lss_client_types_info/aliases b/tests/integration/targets/zpa_lss_client_types_info/aliases new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_lss_client_types_info/defaults/main.yml b/tests/integration/targets/zpa_lss_client_types_info/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_lss_client_types_info/tasks/main.yml b/tests/integration/targets/zpa_lss_client_types_info/tasks/main.yml new file mode 100644 index 0000000..1ce6f72 --- /dev/null +++ b/tests/integration/targets/zpa_lss_client_types_info/tasks/main.yml @@ -0,0 +1,25 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + + - name: Gather LSS Client Types Information + zscaler.zpacloud.zpa_lss_client_types_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + register: result + + - name: Verify lss client types info fetched + ansible.builtin.assert: + that: + - not result.changed + - result.data is defined \ No newline at end of file diff --git a/tests/integration/targets/zpa_lss_config_log_types_formats_info/aliases b/tests/integration/targets/zpa_lss_config_log_types_formats_info/aliases new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_lss_config_log_types_formats_info/defaults/main.yml b/tests/integration/targets/zpa_lss_config_log_types_formats_info/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_lss_config_log_types_formats_info/tasks/main.yml b/tests/integration/targets/zpa_lss_config_log_types_formats_info/tasks/main.yml new file mode 100644 index 0000000..38db23b --- /dev/null +++ b/tests/integration/targets/zpa_lss_config_log_types_formats_info/tasks/main.yml @@ -0,0 +1,117 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + + - name: Gather LSS Log Type zpn_trans_log Information + zscaler.zpacloud.zpa_lss_config_log_types_formats_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + log_type: "zpn_trans_log" + register: zpn_trans_log + + - name: Verify lss log type zpn_trans_log fetched + ansible.builtin.assert: + that: + - zpn_trans_log.data.json is defined + - zpn_trans_log.data.csv is defined + - zpn_trans_log.data.tsv is defined + + - name: Gather LSS Log Type zpn_auth_log Information + zscaler.zpacloud.zpa_lss_config_log_types_formats_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + log_type: "zpn_auth_log" + register: zpn_auth_log + + - name: Verify lss log type zpn_auth_log fetched + ansible.builtin.assert: + that: + - zpn_auth_log.data.json is defined + - zpn_auth_log.data.csv is defined + - zpn_auth_log.data.tsv is defined + + - name: Gather LSS Log Type zpn_ast_auth_log Information + zscaler.zpacloud.zpa_lss_config_log_types_formats_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + log_type: "zpn_ast_auth_log" + register: zpn_ast_auth_log + + - name: Verify lss log type zpn_ast_auth_log fetched + ansible.builtin.assert: + that: + - zpn_ast_auth_log.data.json is defined + - zpn_ast_auth_log.data.csv is defined + - zpn_ast_auth_log.data.tsv is defined + + - name: Gather LSS Log Type zpn_http_trans_log Information + zscaler.zpacloud.zpa_lss_config_log_types_formats_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + log_type: "zpn_http_trans_log" + register: zpn_http_trans_log + + - name: Verify lss log type zpn_http_trans_log fetched + ansible.builtin.assert: + that: + - zpn_http_trans_log.data.json is defined + - zpn_http_trans_log.data.csv is defined + - zpn_http_trans_log.data.tsv is defined + + - name: Gather LSS Log Type zpn_audit_log Information + zscaler.zpacloud.zpa_lss_config_log_types_formats_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + log_type: "zpn_audit_log" + register: zpn_audit_log + + - name: Verify lss log type zpn_audit_log fetched + ansible.builtin.assert: + that: + - zpn_audit_log.data.json is defined + - zpn_audit_log.data.csv is defined + - zpn_audit_log.data.tsv is defined + + - name: Gather LSS Log Type zpn_ast_comprehensive_stats Information + zscaler.zpacloud.zpa_lss_config_log_types_formats_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + log_type: "zpn_ast_comprehensive_stats" + register: zpn_ast_comprehensive_stats + + - name: Verify lss log type zpn_ast_comprehensive_stats fetched + ansible.builtin.assert: + that: + - zpn_ast_comprehensive_stats.data.json is defined + - zpn_ast_comprehensive_stats.data.csv is defined + - zpn_ast_comprehensive_stats.data.tsv is defined + + - name: Gather LSS Log Type zpn_sys_auth_log Information + zscaler.zpacloud.zpa_lss_config_log_types_formats_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + log_type: "zpn_sys_auth_log" + register: zpn_sys_auth_log + + - name: Verify lss log type zpn_sys_auth_log fetched + ansible.builtin.assert: + that: + - zpn_sys_auth_log.data.json is defined + - zpn_sys_auth_log.data.csv is defined + - zpn_sys_auth_log.data.tsv is defined \ No newline at end of file diff --git a/tests/integration/targets/zpa_lss_config_status_codes_info/aliases b/tests/integration/targets/zpa_lss_config_status_codes_info/aliases new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_lss_config_status_codes_info/defaults/main.yml b/tests/integration/targets/zpa_lss_config_status_codes_info/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_lss_config_status_codes_info/tasks/main.yml b/tests/integration/targets/zpa_lss_config_status_codes_info/tasks/main.yml new file mode 100644 index 0000000..ce56e90 --- /dev/null +++ b/tests/integration/targets/zpa_lss_config_status_codes_info/tasks/main.yml @@ -0,0 +1,25 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + + - name: Gather LSS Status Codes Formats Information + zscaler.zpacloud.zpa_lss_config_status_codes_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + register: result + + - name: Verify LSS Status Codes fetched + ansible.builtin.assert: + that: + - not result.changed + - result.data is defined \ No newline at end of file diff --git a/tests/integration/targets/zpa_machine_group_info/aliases b/tests/integration/targets/zpa_machine_group_info/aliases new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_machine_group_info/defaults/main.yml b/tests/integration/targets/zpa_machine_group_info/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_machine_group_info/tasks/main.yml b/tests/integration/targets/zpa_machine_group_info/tasks/main.yml new file mode 100644 index 0000000..111dcf9 --- /dev/null +++ b/tests/integration/targets/zpa_machine_group_info/tasks/main.yml @@ -0,0 +1,25 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + + - name: Gather Machine Group Information + zscaler.zpacloud.zpa_machine_group_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + register: result + + - name: Verify machine group fetched + ansible.builtin.assert: + that: + - not result.changed + - result.data is defined \ No newline at end of file diff --git a/tests/integration/targets/zpa_policy_access_rule/aliases b/tests/integration/targets/zpa_policy_access_rule/aliases new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_policy_access_rule/defaults/main.yml b/tests/integration/targets/zpa_policy_access_rule/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_policy_access_rule/tasks/main.yml b/tests/integration/targets/zpa_policy_access_rule/tasks/main.yml new file mode 100644 index 0000000..f4998ad --- /dev/null +++ b/tests/integration/targets/zpa_policy_access_rule/tasks/main.yml @@ -0,0 +1,123 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + - name: Ensure Policy Access is absent (leftover) + zscaler.zpacloud.zpa_policy_access_rule: + state: absent + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "Test policy access rule" + description: "Test policy access rule" + action: "ALLOW" + rule_order: 1 + operator: "AND" + ignore_errors: true # In case one was left from previous run + register: result + - name: Ensure Policy Access is absent + zscaler.zpacloud.zpa_policy_access_rule: + state: absent + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "Test policy access rule" + description: "Test policy access rule" + action: "ALLOW" + rule_order: 1 + operator: "AND" + ignore_errors: true # In case one was left from previous run + register: result + - name: Verify Policy Access is absent + ansible.builtin.assert: + that: + - not result.changed + # Create Policy Access (Present) + - name: Ensure Policy Access is (Present) + zscaler.zpacloud.zpa_policy_access_rule: + state: present + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "Test policy access rule" + description: "Test policy access rule" + action: "ALLOW" + rule_order: 1 + operator: "AND" + register: result + - name: Verify Policy Access is present + ansible.builtin.assert: + that: + - result.data + - result.data.name is defined + - result.data.id is defined + - name: Create the Policy Access (again; idempotency check) + zscaler.zpacloud.zpa_policy_access_rule: + state: present + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "Test policy access rule" + description: "Test policy access rule" + action: "ALLOW" + rule_order: 1 + operator: "AND" + register: result + - name: Verify Policy Access is absent (from absent) + ansible.builtin.assert: + that: + - result.changed + - name: Fetch all Policy Access + zscaler.zpacloud.zpa_policy_access_rule_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + register: result + - name: Ensure all Policy Access found + ansible.builtin.assert: + that: + - not result.changed + - result.data[0] is defined + - name: Give the ZPA Cloud a 5 seconds to settle + ansible.builtin.pause: + seconds: 5 + - name: Delete Policy Access + zscaler.zpacloud.zpa_policy_access_rule: + state: absent + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "Test policy access rule" + description: "Test policy access rule" + action: "ALLOW" + rule_order: 1 + operator: "AND" + register: result + - name: Verify Policy Access is absent + ansible.builtin.assert: + that: + - result.changed + - name: Delete Policy Access (again; idempotency check) + zscaler.zpacloud.zpa_policy_access_rule: + state: absent + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "Test policy access rule" + description: "Test policy access rule" + action: "ALLOW" + rule_order: 1 + operator: "AND" + register: result + - name: Verify Policy Access is absent + ansible.builtin.assert: + that: + - not result.changed diff --git a/tests/integration/targets/zpa_policy_access_rule_info/aliases b/tests/integration/targets/zpa_policy_access_rule_info/aliases new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_policy_access_rule_info/defaults/main.yml b/tests/integration/targets/zpa_policy_access_rule_info/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_policy_access_rule_info/tasks/main.yml b/tests/integration/targets/zpa_policy_access_rule_info/tasks/main.yml new file mode 100644 index 0000000..6ccf235 --- /dev/null +++ b/tests/integration/targets/zpa_policy_access_rule_info/tasks/main.yml @@ -0,0 +1,25 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + + - name: Gather Policy Access Rule Information + zscaler.zpacloud.zpa_policy_access_rule_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + register: result + + - name: Verify policy access rule fetched + ansible.builtin.assert: + that: + - not result.changed + - result.data is defined diff --git a/tests/integration/targets/zpa_posture_profile_info/aliases b/tests/integration/targets/zpa_posture_profile_info/aliases new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_posture_profile_info/defaults/main.yml b/tests/integration/targets/zpa_posture_profile_info/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_posture_profile_info/tasks/main.yml b/tests/integration/targets/zpa_posture_profile_info/tasks/main.yml new file mode 100644 index 0000000..24c6a26 --- /dev/null +++ b/tests/integration/targets/zpa_posture_profile_info/tasks/main.yml @@ -0,0 +1,25 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + + - name: Gather Posture Profile Information + zscaler.zpacloud.zpa_posture_profile_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + register: result + + - name: Verify posture profile fetched + ansible.builtin.assert: + that: + - not result.changed + - result.data is defined \ No newline at end of file diff --git a/tests/integration/targets/zpa_provisioning_key/aliases b/tests/integration/targets/zpa_provisioning_key/aliases new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_provisioning_key/defaults/main.yml b/tests/integration/targets/zpa_provisioning_key/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_provisioning_key/tasks/main.yml b/tests/integration/targets/zpa_provisioning_key/tasks/main.yml new file mode 100644 index 0000000..9110810 --- /dev/null +++ b/tests/integration/targets/zpa_provisioning_key/tasks/main.yml @@ -0,0 +1,99 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + +# Test Connector Group Provisioning Key + - name: Gather Information Details of All Enrollment Certificates + zscaler.zpacloud.zpa_enrollement_certificate_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "Connector" + register: connector_cert_id + + - name: Create Dummy App Connector Group + zscaler.zpacloud.zpa_app_connector_groups: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + state: "present" + name: "Dummy App Connector Group" + description: "Dummy App Connector Group" + enabled: true + city_country: California, US + country_code: US + latitude: "37.3382082" + longitude: "-121.8863286" + location: "San Jose, CA, USA" + upgrade_day: "SUNDAY" + upgrade_time_in_secs: "66600" + override_version_profile: true + version_profile_id: "1" + dns_query_type: "IPV4" + register: app_connector_group + + - name: Create Dummy Connector Group Provisioning Key + zscaler.zpacloud.zpa_provisioning_key: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + state: "present" + name: "Dummy CONNECTOR_GRP Provisioning Key" + association_type: "CONNECTOR_GRP" + enabled: true + max_usage: "2" + enrollment_cert_id: "{{ connector_cert_id.data[0].id }}" + zcomponent_id: "{{ app_connector_group.data.id }}" + register: result + + - name: Verify Provisioning Keys found + ansible.builtin.assert: + that: + - not result.changed + - result.data is defined + - result.data.name is defined + - result.data.provisioning_key is defined + - result.data.max_usage | length > 0 + - result.data.enrollment_cert_name is defined + - result.data.enrollment_cert_name == "Connector" + + - name: Delete Dummy Connector Group Provisioning Key + zscaler.zpacloud.zpa_provisioning_key: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + state: "absent" + name: "Dummy CONNECTOR_GRP Provisioning Key" + association_type: "CONNECTOR_GRP" + max_usage: "2" + enrollment_cert_id: "{{ connector_cert_id.data[0].id }}" + zcomponent_id: "{{ app_connector_group.data.id }}" + register: result + + - name: Verify Provisioning Key deleted + ansible.builtin.assert: + that: + - result is not changed + + - name: Delete Dummy App Connector Group + zscaler.zpacloud.zpa_app_connector_groups: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + state: "absent" + name: "Dummy App Connector Group" + register: result + + - name: Verify App Connector Group deleted + ansible.builtin.assert: + that: + - result is changed \ No newline at end of file diff --git a/tests/integration/targets/zpa_saml_attribute_info/aliases b/tests/integration/targets/zpa_saml_attribute_info/aliases new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_saml_attribute_info/defaults/main.yml b/tests/integration/targets/zpa_saml_attribute_info/defaults/main.yml new file mode 100644 index 0000000..4b4ffa4 --- /dev/null +++ b/tests/integration/targets/zpa_saml_attribute_info/defaults/main.yml @@ -0,0 +1,2 @@ +name: DepartmentName_SGIO-User-Okta +idp_name: SGIO-User-Okta \ No newline at end of file diff --git a/tests/integration/targets/zpa_saml_attribute_info/tasks/main.yml b/tests/integration/targets/zpa_saml_attribute_info/tasks/main.yml new file mode 100644 index 0000000..72adaaf --- /dev/null +++ b/tests/integration/targets/zpa_saml_attribute_info/tasks/main.yml @@ -0,0 +1,33 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + + - name: Gather Saml Attribute Information + zscaler.zpacloud.zpa_saml_attribute_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "{{ name }}" + register: result + + - debug: + msg: "{{ result }}" + + - name: Verify Saml Attribute found + ansible.builtin.assert: + that: + - not result.changed + - result.data is defined + - result.data[0].name is defined + - result.data[0].name == "{{ name }}" + - result.data[0].idp_name is defined + - result.data[0].idp_name == "{{ idp_name }}" \ No newline at end of file diff --git a/tests/integration/targets/zpa_scim_attribute_header_info/aliases b/tests/integration/targets/zpa_scim_attribute_header_info/aliases new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_scim_attribute_header_info/defaults/main.yml b/tests/integration/targets/zpa_scim_attribute_header_info/defaults/main.yml new file mode 100644 index 0000000..bfcfbc2 --- /dev/null +++ b/tests/integration/targets/zpa_scim_attribute_header_info/defaults/main.yml @@ -0,0 +1 @@ +idp_name: SGIO-User-Okta \ No newline at end of file diff --git a/tests/integration/targets/zpa_scim_attribute_header_info/tasks/main.yml b/tests/integration/targets/zpa_scim_attribute_header_info/tasks/main.yml new file mode 100644 index 0000000..229eb42 --- /dev/null +++ b/tests/integration/targets/zpa_scim_attribute_header_info/tasks/main.yml @@ -0,0 +1,27 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + + - name: Gather Scim Attribute Header Information + zscaler.zpacloud.zpa_scim_attribute_header_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + idp_name: "{{ idp_name }}" + register: result + + - name: Verify scim attribute header fetched + ansible.builtin.assert: + that: + - not result.changed + - result.data is defined + - result.data[0].name is defined \ No newline at end of file diff --git a/tests/integration/targets/zpa_scim_group_info/aliases b/tests/integration/targets/zpa_scim_group_info/aliases new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_scim_group_info/defaults/main.yml b/tests/integration/targets/zpa_scim_group_info/defaults/main.yml new file mode 100644 index 0000000..bfcfbc2 --- /dev/null +++ b/tests/integration/targets/zpa_scim_group_info/defaults/main.yml @@ -0,0 +1 @@ +idp_name: SGIO-User-Okta \ No newline at end of file diff --git a/tests/integration/targets/zpa_scim_group_info/tasks/main.yml b/tests/integration/targets/zpa_scim_group_info/tasks/main.yml new file mode 100644 index 0000000..b5dc026 --- /dev/null +++ b/tests/integration/targets/zpa_scim_group_info/tasks/main.yml @@ -0,0 +1,27 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + + - name: Gather Scim Group Information + zscaler.zpacloud.zpa_scim_group_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + idp_name: "{{ idp_name }}" + register: result + + - name: Verify scim group fetched + ansible.builtin.assert: + that: + - not result.changed + - result.data is defined + - result.data[0].name is defined \ No newline at end of file diff --git a/tests/integration/targets/zpa_segment_group/defaults/main.yml b/tests/integration/targets/zpa_segment_group/defaults/main.yml new file mode 100644 index 0000000..79535c3 --- /dev/null +++ b/tests/integration/targets/zpa_segment_group/defaults/main.yml @@ -0,0 +1,6 @@ +config_space: "DEFAULT" +name: Dummy Application group for integration test +description: Dummy Application group for integration test +enabled: true +policy_migrated: true +tcp_keep_alive_enabled: "1" diff --git a/tests/integration/targets/zpa_segment_group/tasks/main.yml b/tests/integration/targets/zpa_segment_group/tasks/main.yml new file mode 100644 index 0000000..e0f8bb6 --- /dev/null +++ b/tests/integration/targets/zpa_segment_group/tasks/main.yml @@ -0,0 +1,129 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + - name: Ensure Segment Group is absent (leftover) + zscaler.zpacloud.zpa_segment_group: + state: absent + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + config_space: "{{ config_space }}" + name: "{{ name }}" + description: "{{ description }}" + enabled: "{{ enabled }}" + policy_migrated: "{{ policy_migrated }}" + tcp_keep_alive_enabled: "{{ tcp_keep_alive_enabled }}" + ignore_errors: true # In case one was left from previous run + register: result + - name: Ensure Segment Group is absent + zscaler.zpacloud.zpa_segment_group: + state: absent + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + config_space: "{{ config_space }}" + name: "{{ name }}" + description: "{{ description }}" + enabled: "{{ enabled }}" + policy_migrated: "{{ policy_migrated }}" + tcp_keep_alive_enabled: "{{ tcp_keep_alive_enabled }}" + ignore_errors: true # In case one was left from previous run + register: result + - name: Verify Segment Group is absent + ansible.builtin.assert: + that: + - not result.changed + # Create Segment Groups (Present) + - name: Ensure Segment Group is (Present) + zscaler.zpacloud.zpa_segment_group: + state: present + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + config_space: "{{ config_space }}" + name: "{{ name }}" + description: "{{ description }}" + enabled: "{{ enabled }}" + policy_migrated: "{{ policy_migrated }}" + tcp_keep_alive_enabled: "{{ tcp_keep_alive_enabled }}" + register: result + - name: Verify Segment Group is present + ansible.builtin.assert: + that: + - result.data + - result.data.name is defined + - result.data.enabled is true + - name: Create the Segment Group (again; idempotency check) + zscaler.zpacloud.zpa_segment_group: + state: present + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + config_space: "{{ config_space }}" + name: "{{ name }}" + description: "{{ description }}" + enabled: "{{ enabled }}" + policy_migrated: "{{ policy_migrated }}" + tcp_keep_alive_enabled: "{{ tcp_keep_alive_enabled }}" + register: result + - name: Verify Segment Group is present (again; idempotency check) + ansible.builtin.assert: + that: + - result.changed + - name: Fetch all Segment Groups + zscaler.zpacloud.zpa_segment_group_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + register: result + - name: Ensure all Segment Groups found + ansible.builtin.assert: + that: + - not result.changed + - result.data[0] is defined + - name: Give the ZPA Cloud a 5 seconds to settle + ansible.builtin.pause: + seconds: 5 + - name: Delete Segment Group + zscaler.zpacloud.zpa_segment_group: + state: absent + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + config_space: "{{ config_space }}" + name: "{{ name }}" + description: "{{ description }}" + enabled: "{{ enabled }}" + policy_migrated: "{{ policy_migrated }}" + tcp_keep_alive_enabled: "{{ tcp_keep_alive_enabled }}" + register: result + - name: Verify Segment Group is absent + ansible.builtin.assert: + that: + - result.changed + - name: Delete Segment Group (again; idempotency check) + zscaler.zpacloud.zpa_segment_group: + state: absent + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + config_space: "{{ config_space }}" + name: "{{ name }}" + description: "{{ description }}" + enabled: "{{ enabled }}" + policy_migrated: "{{ policy_migrated }}" + tcp_keep_alive_enabled: "{{ tcp_keep_alive_enabled }}" + register: result + - name: Verify Segment Group is absent + ansible.builtin.assert: + that: + - not result.changed diff --git a/tests/integration/targets/zpa_segment_group_info/defaults/main.yml b/tests/integration/targets/zpa_segment_group_info/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_segment_group_info/tasks/main.yml b/tests/integration/targets/zpa_segment_group_info/tasks/main.yml new file mode 100644 index 0000000..cdfe33c --- /dev/null +++ b/tests/integration/targets/zpa_segment_group_info/tasks/main.yml @@ -0,0 +1,25 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + + - name: Gather Segment Group Information + zscaler.zpacloud.zpa_segment_group_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + register: result + + - name: Verify Segment Group fetched + ansible.builtin.assert: + that: + - not result.changed + - result.data is defined diff --git a/tests/integration/targets/zpa_server_group/defaults/main.yml b/tests/integration/targets/zpa_server_group/defaults/main.yml new file mode 100644 index 0000000..5853ba1 --- /dev/null +++ b/tests/integration/targets/zpa_server_group/defaults/main.yml @@ -0,0 +1,4 @@ +name: "Dummy server group for integration test" +description: "Dummy server group for integration test" +enabled: true +dynamic_discovery: true diff --git a/tests/integration/targets/zpa_server_group/tasks/main.yml b/tests/integration/targets/zpa_server_group/tasks/main.yml new file mode 100644 index 0000000..256d583 --- /dev/null +++ b/tests/integration/targets/zpa_server_group/tasks/main.yml @@ -0,0 +1,181 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + - name: Create the App Connector Group + zscaler.zpacloud.zpa_app_connector_groups: + state: present + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "Dummy Application connector group for integration test" + description: "Dummy Application connector group for integration test" + enabled: true + city_country: California, US + country_code: US + latitude: 37.3382082 + longitude: -121.8863286 + location: San Jose, CA, USA + upgrade_day: SUNDAY + upgrade_time_in_secs: 66600 + override_version_profile: true + version_profile_id: "1" + dns_query_type: "IPV4" + register: app_connector_group + - name: Verify Application connector group is created + ansible.builtin.assert: + that: + - app_connector_group.data + - app_connector_group.data.name is defined + - app_connector_group.data.id is defined + - app_connector_group.data.enabled is true + - name: Ensure Server Group is absent (leftover) + zscaler.zpacloud.zpa_server_group: + state: absent + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "{{ name }}" + description: "{{ description }}" + enabled: "{{ enabled }}" + dynamic_discovery: "{{ dynamic_discovery }}" + app_connector_groups: + - id: "{{app_connector_group.data.id}}" + ignore_errors: true # In case one was left from previous run + register: result + - name: Ensure Server Group is absent + zscaler.zpacloud.zpa_server_group: + state: absent + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "{{ name }}" + description: "{{ description }}" + enabled: "{{ enabled }}" + dynamic_discovery: "{{ dynamic_discovery }}" + app_connector_groups: + - id: "{{app_connector_group.data.id}}" + ignore_errors: true # In case one was left from previous run + register: result + - name: Verify Server Group is absent + ansible.builtin.assert: + that: + - not result.changed + # Create Server Groups (Present) + - name: Ensure Server Group is (Present) + zscaler.zpacloud.zpa_server_group: + state: present + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "{{ name }}" + description: "{{ description }}" + enabled: "{{ enabled }}" + dynamic_discovery: "{{ dynamic_discovery }}" + app_connector_groups: + - id: "{{app_connector_group.data.id}}" + register: result + - name: Verify Server Group is present + ansible.builtin.assert: + that: + - result.data + - result.data.name is defined + - result.data.enabled is true + - name: Create the Server Group (again; idempotency check) + zscaler.zpacloud.zpa_server_group: + state: present + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "{{ name }}" + description: "{{ description }}" + enabled: "{{ enabled }}" + dynamic_discovery: "{{ dynamic_discovery }}" + app_connector_groups: + - id: "{{app_connector_group.data.id}}" + register: result + - name: Verify Server Group is present (again; idempotency check) + ansible.builtin.assert: + that: + - result.changed + - name: Fetch all Server Groups + zscaler.zpacloud.zpa_server_group_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + register: result + - name: Ensure all Server Groups found + ansible.builtin.assert: + that: + - not result.changed + - result.data[0] is defined + - name: Give the ZPA Cloud a 5 seconds to settle + ansible.builtin.pause: + seconds: 5 + - name: Delete Server Group + zscaler.zpacloud.zpa_server_group: + state: absent + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "{{ name }}" + description: "{{ description }}" + enabled: "{{ enabled }}" + dynamic_discovery: "{{ dynamic_discovery }}" + app_connector_groups: + - id: "{{app_connector_group.data.id}}" + register: result + - name: Verify Server Group is absent + ansible.builtin.assert: + that: + - result.changed + - name: Delete Server Group (again; idempotency check) + zscaler.zpacloud.zpa_server_group: + state: absent + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "{{ name }}" + description: "{{ description }}" + enabled: "{{ enabled }}" + dynamic_discovery: "{{ dynamic_discovery }}" + app_connector_groups: + - id: "{{app_connector_group.data.id}}" + register: result + - name: Verify Server Group is absent + ansible.builtin.assert: + that: + - not result.changed + - name: Delete App connector Group + zscaler.zpacloud.zpa_app_connector_groups: + state: absent + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "{{app_connector_group.data.name}}" + id: "{{app_connector_group.data.id}}" + description: "Dummy Application connector group for integration test" + enabled: true + city_country: California, US + country_code: US + latitude: 37.3382082 + longitude: -121.8863286 + location: San Jose, CA, USA + upgrade_day: SUNDAY + upgrade_time_in_secs: 66600 + override_version_profile: true + version_profile_id: "1" + dns_query_type: "IPV4" + register: result + - name: Verify App connector Group is absent + ansible.builtin.assert: + that: + - result.changed diff --git a/tests/integration/targets/zpa_server_group_info/aliases b/tests/integration/targets/zpa_server_group_info/aliases new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_server_group_info/defaults/main.yml b/tests/integration/targets/zpa_server_group_info/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_server_group_info/tasks/main.yml b/tests/integration/targets/zpa_server_group_info/tasks/main.yml new file mode 100644 index 0000000..089f5f4 --- /dev/null +++ b/tests/integration/targets/zpa_server_group_info/tasks/main.yml @@ -0,0 +1,25 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + + - name: Gather Server Group Information + zscaler.zpacloud.zpa_server_group_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + register: result + + - name: Verify Server Group fetched + ansible.builtin.assert: + that: + - not result.changed + - result.data is defined diff --git a/tests/integration/targets/zpa_service_edge_groups/defaults/main.yml b/tests/integration/targets/zpa_service_edge_groups/defaults/main.yml new file mode 100644 index 0000000..e4929c5 --- /dev/null +++ b/tests/integration/targets/zpa_service_edge_groups/defaults/main.yml @@ -0,0 +1,12 @@ +name: san_jose +description: san_jose +enabled: true +city_country: California, US +country_code: US +latitude: 37.3382082 +longitude: -121.8863286 +location: San Jose, CA, USA +upgrade_day: SUNDAY +upgrade_time_in_secs: 66600 +override_version_profile: true +version_profile_id: "2" \ No newline at end of file diff --git a/tests/integration/targets/zpa_service_edge_groups/tasks/main.yml b/tests/integration/targets/zpa_service_edge_groups/tasks/main.yml new file mode 100644 index 0000000..2639b5c --- /dev/null +++ b/tests/integration/targets/zpa_service_edge_groups/tasks/main.yml @@ -0,0 +1,148 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + + - name: Ensure Service Edge Group is Absent (leftover) + zscaler.zpacloud.zpa_service_edge_groups: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + state: absent + name: "{{ name }}" + ignore_errors: true # In case one was left from previous run + register: result + + - name: Ensure Service Edge Group is Absent + zscaler.zpacloud.zpa_service_edge_groups: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + state: absent + name: "{{ name }}" + register: result + + - name: Verify Service Edge Group is absent + ansible.builtin.assert: + that: + - not result.changed + + - name: Create Service Edge Group is (present) + zscaler.zpacloud.zpa_service_edge_groups: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + state: present + name: "{{ name }}" + description: "{{ description }}" + enabled: "{{ enabled }}" + city_country: "{{ city_country }}" + country_code: "{{ country_code }}" + latitude: "{{ latitude }}" + longitude: "{{ longitude }}" + location: "{{ location }}" + upgrade_day: "{{ upgrade_day }}" + upgrade_time_in_secs: "{{ upgrade_time_in_secs }}" + override_version_profile: "{{ override_version_profile }}" + version_profile_id: "{{ version_profile_id }}" + register: result + + - name: Verify Service Edge Group is present + ansible.builtin.assert: + that: + - not result.changed + - result.data is defined + - result.data.name is defined + - result.data.name == name + - result.data.enabled == true + + - name: Create Service Edge Group is (again; idempotency check) + zscaler.zpacloud.zpa_service_edge_groups: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + state: present + name: "{{ name }}" + description: "{{ description }}" + enabled: "{{ enabled }}" + city_country: "{{ city_country }}" + country_code: "{{ country_code }}" + latitude: "{{ latitude }}" + longitude: "{{ longitude }}" + location: "{{ location }}" + upgrade_day: "{{ upgrade_day }}" + upgrade_time_in_secs: "{{ upgrade_time_in_secs }}" + override_version_profile: "{{ override_version_profile }}" + version_profile_id: "{{ version_profile_id }}" + register: result + + - name: Verify Service Edge Group is present + ansible.builtin.assert: + that: + - result.changed + + - name: Fetch All Service Edge Group + zscaler.zpacloud.zpa_service_edge_groups_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + register: result + + - name: Ensure all Service Edge Group found + ansible.builtin.assert: + that: + - not result.changed + - result.data is defined + + - name: Fetch this Service Edge Group + zscaler.zpacloud.zpa_service_edge_groups_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + name: "{{ name }}" + register: result + + - name: Ensure this Service Edge Group is found + ansible.builtin.assert: + that: + - not result.changed + - result.data is defined + - result.data[0].name is defined + - result.data[0].name == name + - result.data[0].enabled == true + + - name: Delete Service Edge Group + zscaler.zpacloud.zpa_service_edge_groups: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + state: absent + name: "{{ name }}" + register: result + + - name: Verify Service Edge Group is Deleted + ansible.builtin.assert: + that: + - not result.changed + + - name: Delete Service Edge Group (again; idempotency check) + zscaler.zpacloud.zpa_service_edge_groups: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + state: absent + name: "{{ name }}" + register: result + + - name: Verify the Service Edge Group is deleted + ansible.builtin.assert: + that: + - not result.changed \ No newline at end of file diff --git a/tests/integration/targets/zpa_service_edge_groups_info/aliases b/tests/integration/targets/zpa_service_edge_groups_info/aliases new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_service_edge_groups_info/defaults/main.yml b/tests/integration/targets/zpa_service_edge_groups_info/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_service_edge_groups_info/tasks/main.yml b/tests/integration/targets/zpa_service_edge_groups_info/tasks/main.yml new file mode 100644 index 0000000..4e067c1 --- /dev/null +++ b/tests/integration/targets/zpa_service_edge_groups_info/tasks/main.yml @@ -0,0 +1,25 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + + - name: Gather Service Edge Groups Information + zscaler.zpacloud.zpa_service_edge_groups_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + register: result + + - name: Verify service edge groups fetched + ansible.builtin.assert: + that: + - not result.changed + - result.data is defined \ No newline at end of file diff --git a/tests/integration/targets/zpa_trusted_networks_info/aliases b/tests/integration/targets/zpa_trusted_networks_info/aliases new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_trusted_networks_info/defaults/main.yml b/tests/integration/targets/zpa_trusted_networks_info/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/targets/zpa_trusted_networks_info/tasks/main.yml b/tests/integration/targets/zpa_trusted_networks_info/tasks/main.yml new file mode 100644 index 0000000..0918e8a --- /dev/null +++ b/tests/integration/targets/zpa_trusted_networks_info/tasks/main.yml @@ -0,0 +1,25 @@ +--- +- block: + - name: Ensure ZPA API Credential is Set + ansible.builtin.fail: + msg: client_id, client_secret and customer_id should be defined in tests/integration/integration_config.yml + when: + - client_id is not defined + - client_secret is not defined + - customer_id is not defined + - client_id | length == 0 + - client_secret | length == 0 + - customer_id | length == 0 + + - name: Gather Trusted Network Information + zscaler.zpacloud.zpa_trusted_networks_info: + client_id: "{{ client_id }}" + client_secret: "{{ client_secret }}" + customer_id: "{{ customer_id }}" + register: result + + - name: Verify trusted network fetched + ansible.builtin.assert: + that: + - not result.changed + - result.data is defined \ No newline at end of file diff --git a/tests/unit/plugins/module_utils/test_zpa_app_connector_group.py b/tests/unit/plugins/module_utils/test_zpa_app_connector_group.py new file mode 100644 index 0000000..77000bd --- /dev/null +++ b/tests/unit/plugins/module_utils/test_zpa_app_connector_group.py @@ -0,0 +1,107 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +import unittest +from unittest.mock import MagicMock + +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_app_connector_group import ( + AppConnectorGroupService, +) + + +class TestAppConnectorGroupService(unittest.TestCase): + def test_get_by_id_when_ok(self): + module = MagicMock() + rest = MagicMock() + rest.get = MagicMock() + rest.get.return_value.status_code = 200 + rest.get.return_value.json = { + "name": "bar", + "id": "test", + "connectors": [{"someField": "value"}], + } + k = AppConnectorGroupService(module, "", rest) + self.assertEqual( + k.getByID("test"), + {"name": "bar", "id": "test", "connectors": [{"some_field": "value"}]}, + ) + + def test_get_by_id_when_nok(self): + module = MagicMock() + rest = MagicMock() + rest.get = MagicMock() + rest.get.return_value.status_code = 400 + k = AppConnectorGroupService(module, "", rest) + self.assertIsNone(k.getByID("test")) + + def test_get_by_name_when_ok(self): + module = MagicMock() + rest = MagicMock() + rest.get_paginated_data = MagicMock() + rest.get_paginated_data.return_value = [ + {"name": "bar1", "id": "test1", "connectors": [{"someField": "value1"}]}, + {"name": "bar2", "id": "test2", "connectors": [{"someField": "value2"}]}, + ] + k = AppConnectorGroupService(module, "", rest) + self.assertEqual( + k.getByName("bar2"), + {"name": "bar2", "id": "test2", "connectors": [{"some_field": "value2"}]}, + ) + + def test_get_by_name_when_nok(self): + module = MagicMock() + rest = MagicMock() + rest.get = MagicMock() + rest.get.return_value.status_code = 400 + k = AppConnectorGroupService(module, "", rest) + self.assertIsNone(k.getByName("test")) + + def test_create_when_ok(self): + jsonObj = {"name": "bar", "id": "test", "connectors": [{"someField": "value"}]} + obj = {"name": "bar", "id": "test", "connectors": [{"some_field": "value"}]} + module = MagicMock() + rest = MagicMock() + rest.post = MagicMock() + rest.post.return_value.status_code = 200 + rest.post.return_value.json = jsonObj + k = AppConnectorGroupService(module, "", rest) + self.assertEqual(k.create(obj), obj) + + def test_create_when_nok(self): + module = MagicMock() + rest = MagicMock() + rest.post = MagicMock() + rest.post.return_value.status_code = 400 + k = AppConnectorGroupService(module, "", rest) + self.assertIsNone( + k.create( + {"name": "bar", "id": "test", "connectors": [{"someField": "value"}]} + ) + ) + + def test_update_when_ok(self): + jsonObj = {"name": "bar", "id": "test", "connectors": [{"someField": "value"}]} + obj = {"name": "bar", "id": "test", "connectors": [{"some_field": "value"}]} + module = MagicMock() + rest = MagicMock() + rest.put = MagicMock() + rest.put.return_value.status_code = 200 + # For getById(...) + rest.get = MagicMock() + rest.get.return_value.status_code = 200 + rest.get.return_value.json = jsonObj + k = AppConnectorGroupService(module, "", rest) + self.assertEqual(k.update(obj), obj) + + def test_update_when_nok(self): + module = MagicMock() + rest = MagicMock() + rest.put = MagicMock() + rest.put.return_value.status_code = 400 + k = AppConnectorGroupService(module, "", rest) + self.assertIsNone( + k.update( + {"name": "bar", "id": "test", "connectors": [{"someField": "value"}]} + ) + ) diff --git a/tests/unit/plugins/module_utils/test_zpa_application_segment.py b/tests/unit/plugins/module_utils/test_zpa_application_segment.py new file mode 100644 index 0000000..edba66d --- /dev/null +++ b/tests/unit/plugins/module_utils/test_zpa_application_segment.py @@ -0,0 +1,123 @@ +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +import unittest +from unittest.mock import MagicMock +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_application_segment import ( + ApplicationSegmentService, +) + + +class TestApplicationSegmentService(unittest.TestCase): + def test_get_by_id_when_ok(self): + module = MagicMock() + rest = MagicMock() + rest.get = MagicMock() + rest.get.return_value.status_code = 200 + rest.get.return_value.json = { + "name": "bar", + "id": "test", + "server_groups": [], + "clientless_apps": [], + } + k = ApplicationSegmentService(module, "", rest) + self.assertEqual( + k.getByID("test"), + {"name": "bar", "id": "test", "server_groups": [], "clientless_apps": []}, + ) + + def test_get_by_id_when_nok(self): + module = MagicMock() + rest = MagicMock() + rest.get = MagicMock() + rest.get.return_value.status_code = 400 + k = ApplicationSegmentService(module, "", rest) + self.assertIsNone(k.getByID("test")) + + def test_get_by_name_when_ok(self): + module = MagicMock() + rest = MagicMock() + rest.get_paginated_data = MagicMock() + rest.get_paginated_data.return_value = [ + {"name": "bar1", "id": "test1", "server_groups": [], "clientless_apps": []}, + {"name": "bar2", "id": "test2", "server_groups": [], "clientless_apps": []}, + ] + k = ApplicationSegmentService(module, "", rest) + self.assertEqual( + k.getByName("bar2"), + {"name": "bar2", "id": "test2", "server_groups": [], "clientless_apps": []}, + ) + + def test_get_by_name_when_nok(self): + module = MagicMock() + rest = MagicMock() + rest.get = MagicMock() + rest.get.return_value.status_code = 400 + k = ApplicationSegmentService(module, "", rest) + self.assertIsNone(k.getByName("test")) + + def test_create_when_ok(self): + jsonObj = { + "name": "bar", + "id": "test", + "serverGroups": [], + "clientlessApps": [], + } + obj = {"name": "bar", "id": "test", "server_groups": [], "clientless_apps": []} + module = MagicMock() + rest = MagicMock() + rest.post = MagicMock() + rest.post.return_value.status_code = 200 + rest.post.return_value.json = jsonObj + # For getById(...) + rest.get = MagicMock() + rest.get.return_value.status_code = 200 + rest.get.return_value.json = jsonObj + k = ApplicationSegmentService(module, "", rest) + self.assertEqual(k.create(obj), obj) + + def test_create_when_nok(self): + module = MagicMock() + rest = MagicMock() + rest.post = MagicMock() + rest.post.return_value.status_code = 400 + k = ApplicationSegmentService(module, "", rest) + self.assertIsNone( + k.create( + { + "name": "bar", + "id": "test", + "server_groups": [], + "clientless_apps": [], + } + ) + ) + + def test_update_when_ok(self): + jsonObj = { + "name": "bar", + "id": "test", + "serverGroups": [], + "clientlessApps": [], + } + obj = {"name": "bar", "id": "test", "server_groups": [], "clientless_apps": []} + module = MagicMock() + rest = MagicMock() + rest.put = MagicMock() + rest.put.return_value.status_code = 200 + # For getById(...) + rest.get = MagicMock() + rest.get.return_value.status_code = 200 + rest.get.return_value.json = jsonObj + k = ApplicationSegmentService(module, "", rest) + self.assertEqual(k.update(obj), obj) + + def test_update_when_nok(self): + module = MagicMock() + rest = MagicMock() + rest.put = MagicMock() + rest.put.return_value.status_code = 400 + k = ApplicationSegmentService(module, "", rest) + self.assertIsNone(k.update({"name": "bar", "id": "test"})) diff --git a/tests/unit/plugins/module_utils/test_zpa_policy_access_rule.py b/tests/unit/plugins/module_utils/test_zpa_policy_access_rule.py new file mode 100644 index 0000000..705b2ae --- /dev/null +++ b/tests/unit/plugins/module_utils/test_zpa_policy_access_rule.py @@ -0,0 +1,164 @@ +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +import unittest +from unittest.mock import MagicMock +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_policy_access_rule import ( + PolicyAccessRuleService, +) + + +class TestPolicyAccessRuleService(unittest.TestCase): + def test_get_by_id_when_ok(self): + module = MagicMock() + rest = MagicMock() + rest.get = MagicMock() + rest.get.return_value.status_code = 200 + rest.get.return_value.json = { + "name": "bar", + "id": "test", + "app_connector_groups": [], + "conditions": [], + "app_server_groups": [], + } + k = PolicyAccessRuleService(module, "", rest) + self.assertEqual( + k.getByID("test", "1"), + { + "name": "bar", + "id": "test", + "app_connector_groups": [], + "conditions": [], + "app_server_groups": [], + }, + ) + + def test_get_by_id_when_nok(self): + module = MagicMock() + rest = MagicMock() + rest.get = MagicMock() + rest.get.return_value.status_code = 400 + k = PolicyAccessRuleService(module, "", rest) + self.assertIsNone(k.getByID("test", "1")) + + def test_get_by_name_when_ok(self): + module = MagicMock() + rest = MagicMock() + rest.get_paginated_data = MagicMock() + rest.get_paginated_data.return_value = [ + { + "name": "bar1", + "id": "test1", + "app_connector_groups": [], + "conditions": [], + "app_server_groups": [], + }, + { + "name": "bar2", + "id": "test2", + "app_connector_groups": [], + "conditions": [], + "app_server_groups": [], + }, + ] + k = PolicyAccessRuleService(module, "", rest) + self.assertEqual( + k.getByNameAndType("bar2", ""), + { + "name": "bar2", + "id": "test2", + "app_connector_groups": [], + "conditions": [], + "app_server_groups": [], + }, + ) + + def test_get_by_name_when_nok(self): + module = MagicMock() + rest = MagicMock() + rest.get = MagicMock() + rest.get.return_value.status_code = 400 + k = PolicyAccessRuleService(module, "", rest) + self.assertIsNone(k.getByNameAndType("test", "")) + + def test_create_when_ok(self): + jsonObj = { + "name": "bar", + "id": "test", + "appConnectorGroups": [], + "conditions": [], + "appServerGroups": [], + } + obj = { + "name": "bar", + "id": "test", + "app_connector_groups": [], + "conditions": [], + "app_server_groups": [], + } + module = MagicMock() + rest = MagicMock() + rest.post = MagicMock() + rest.post.return_value.status_code = 200 + rest.post.return_value.json = jsonObj + # For getById(...) + rest.get = MagicMock() + rest.get.return_value.status_code = 200 + rest.get.return_value.json = jsonObj + k = PolicyAccessRuleService(module, "", rest) + self.assertEqual(k.create(obj, ""), obj) + + def test_create_when_nok(self): + module = MagicMock() + rest = MagicMock() + rest.post = MagicMock() + rest.post.return_value.status_code = 400 + k = PolicyAccessRuleService(module, "", rest) + self.assertIsNone( + k.create( + { + "name": "bar", + "id": "test", + "app_connector_groups": [], + "conditions": [], + "app_server_groups": [], + }, + "", + ) + ) + + def test_update_when_ok(self): + jsonObj = { + "name": "bar", + "id": "test", + "appConnectorGroups": [], + "conditions": [], + "appServerGroups": [], + } + obj = { + "name": "bar", + "id": "test", + "app_connector_groups": [], + "conditions": [], + "app_server_groups": [], + } + module = MagicMock() + rest = MagicMock() + rest.put = MagicMock() + rest.put.return_value.status_code = 200 + # For getById(...) + rest.get = MagicMock() + rest.get.return_value.status_code = 200 + rest.get.return_value.json = jsonObj + k = PolicyAccessRuleService(module, "", rest) + self.assertEqual(k.update(obj, "1"), obj) + + def test_update_when_nok(self): + module = MagicMock() + rest = MagicMock() + rest.put = MagicMock() + rest.put.return_value.status_code = 400 + k = PolicyAccessRuleService(module, "", rest) + self.assertIsNone(k.update({"name": "bar", "id": "test"}, "1")) diff --git a/tests/unit/plugins/module_utils/test_zpa_segment_group.py b/tests/unit/plugins/module_utils/test_zpa_segment_group.py new file mode 100644 index 0000000..b414a5a --- /dev/null +++ b/tests/unit/plugins/module_utils/test_zpa_segment_group.py @@ -0,0 +1,97 @@ +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +import unittest +from unittest.mock import MagicMock +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_segment_group import ( + SegmentGroupService, +) + + +class TestSegmentGroupService(unittest.TestCase): + def test_get_by_id_when_ok(self): + module = MagicMock() + rest = MagicMock() + rest.get = MagicMock() + rest.get.return_value.status_code = 200 + rest.get.return_value.json = {"name": "bar", "id": "test", "applications": []} + k = SegmentGroupService(module, "", rest) + self.assertEqual( + k.getByID("test"), {"name": "bar", "id": "test", "applications": []} + ) + + def test_get_by_id_when_nok(self): + module = MagicMock() + rest = MagicMock() + rest.get = MagicMock() + rest.get.return_value.status_code = 400 + k = SegmentGroupService(module, "", rest) + self.assertIsNone(k.getByID("test")) + + def test_get_by_name_when_ok(self): + module = MagicMock() + rest = MagicMock() + rest.get_paginated_data = MagicMock() + rest.get_paginated_data.return_value = [ + {"name": "bar1", "id": "test1", "applications": []}, + {"name": "bar2", "id": "test2", "applications": []}, + ] + k = SegmentGroupService(module, "", rest) + self.assertEqual( + k.getByName("bar2"), {"name": "bar2", "id": "test2", "applications": []} + ) + + def test_get_by_name_when_nok(self): + module = MagicMock() + rest = MagicMock() + rest.get = MagicMock() + rest.get.return_value.status_code = 400 + k = SegmentGroupService(module, "", rest) + self.assertIsNone(k.getByName("test")) + + def test_create_when_ok(self): + jsonObj = {"name": "bar", "id": "test", "applications": []} + obj = {"name": "bar", "id": "test", "applications": []} + module = MagicMock() + rest = MagicMock() + rest.post = MagicMock() + rest.post.return_value.status_code = 200 + rest.post.return_value.json = jsonObj + # For getById(...) + rest.get = MagicMock() + rest.get.return_value.status_code = 200 + rest.get.return_value.json = jsonObj + k = SegmentGroupService(module, "", rest) + self.assertEqual(k.create(obj), obj) + + def test_create_when_nok(self): + module = MagicMock() + rest = MagicMock() + rest.post = MagicMock() + rest.post.return_value.status_code = 400 + k = SegmentGroupService(module, "", rest) + self.assertIsNone(k.create({"name": "bar", "id": "test", "applications": []})) + + def test_update_when_ok(self): + jsonObj = {"name": "bar", "id": "test", "applications": []} + obj = {"name": "bar", "id": "test", "applications": []} + module = MagicMock() + rest = MagicMock() + rest.put = MagicMock() + rest.put.return_value.status_code = 200 + # For getById(...) + rest.get = MagicMock() + rest.get.return_value.status_code = 200 + rest.get.return_value.json = jsonObj + k = SegmentGroupService(module, "", rest) + self.assertEqual(k.update(obj), obj) + + def test_update_when_nok(self): + module = MagicMock() + rest = MagicMock() + rest.put = MagicMock() + rest.put.return_value.status_code = 400 + k = SegmentGroupService(module, "", rest) + self.assertIsNone(k.update({"name": "bar", "id": "test"})) diff --git a/tests/unit/plugins/module_utils/test_zpa_server_group.py b/tests/unit/plugins/module_utils/test_zpa_server_group.py new file mode 100644 index 0000000..52475ab --- /dev/null +++ b/tests/unit/plugins/module_utils/test_zpa_server_group.py @@ -0,0 +1,163 @@ +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +import unittest +from unittest.mock import MagicMock +from ansible_collections.zscaler.zpacloud.plugins.module_utils.zpa_server_group import ( + ServerGroupService, +) + + +class TestServerGroupService(unittest.TestCase): + def test_get_by_id_when_ok(self): + module = MagicMock() + rest = MagicMock() + rest.get = MagicMock() + rest.get.return_value.status_code = 200 + rest.get.return_value.json = { + "name": "bar", + "id": "test", + "applications": [], + "app_connector_groups": [], + "servers": [], + } + k = ServerGroupService(module, "", rest) + self.assertEqual( + k.getByID("test"), + { + "name": "bar", + "id": "test", + "applications": [], + "app_connector_groups": [], + "servers": [], + }, + ) + + def test_get_by_id_when_nok(self): + module = MagicMock() + rest = MagicMock() + rest.get = MagicMock() + rest.get.return_value.status_code = 400 + k = ServerGroupService(module, "", rest) + self.assertIsNone(k.getByID("test")) + + def test_get_by_name_when_ok(self): + module = MagicMock() + rest = MagicMock() + rest.get_paginated_data = MagicMock() + rest.get_paginated_data.return_value = [ + { + "name": "bar1", + "id": "test1", + "applications": [], + "app_connector_groups": [], + "servers": [], + }, + { + "name": "bar2", + "id": "test2", + "applications": [], + "app_connector_groups": [], + "servers": [], + }, + ] + k = ServerGroupService(module, "", rest) + self.assertEqual( + k.getByName("bar2"), + { + "name": "bar2", + "id": "test2", + "applications": [], + "app_connector_groups": [], + "servers": [], + }, + ) + + def test_get_by_name_when_nok(self): + module = MagicMock() + rest = MagicMock() + rest.get = MagicMock() + rest.get.return_value.status_code = 400 + k = ServerGroupService(module, "", rest) + self.assertIsNone(k.getByName("test")) + + def test_create_when_ok(self): + jsonObj = { + "name": "bar", + "id": "test", + "applications": [], + "appConnectorGroups": [], + "servers": [], + } + obj = { + "name": "bar", + "id": "test", + "applications": [], + "app_connector_groups": [], + "servers": [], + } + module = MagicMock() + rest = MagicMock() + rest.post = MagicMock() + rest.post.return_value.status_code = 200 + rest.post.return_value.json = jsonObj + # For getById(...) + rest.get = MagicMock() + rest.get.return_value.status_code = 200 + rest.get.return_value.json = jsonObj + k = ServerGroupService(module, "", rest) + self.assertEqual(k.create(obj), obj) + + def test_create_when_nok(self): + module = MagicMock() + rest = MagicMock() + rest.post = MagicMock() + rest.post.return_value.status_code = 400 + k = ServerGroupService(module, "", rest) + self.assertIsNone( + k.create( + { + "name": "bar", + "id": "test", + "applications": [], + "app_connector_groups": [], + "servers": [], + } + ) + ) + + def test_update_when_ok(self): + jsonObj = { + "name": "bar", + "id": "test", + "applications": [], + "appConnectorGroups": [], + "servers": [], + } + obj = { + "name": "bar", + "id": "test", + "applications": [], + "app_connector_groups": [], + "servers": [], + } + module = MagicMock() + rest = MagicMock() + rest.put = MagicMock() + rest.put.return_value.status_code = 200 + # For getById(...) + rest.get = MagicMock() + rest.get.return_value.status_code = 200 + rest.get.return_value.json = jsonObj + k = ServerGroupService(module, "", rest) + self.assertEqual(k.update(obj), obj) + + def test_update_when_nok(self): + module = MagicMock() + rest = MagicMock() + rest.put = MagicMock() + rest.put.return_value.status_code = 400 + k = ServerGroupService(module, "", rest) + self.assertIsNone(k.update({"name": "bar", "id": "test"})) diff --git a/tests/utils/render.py b/tests/utils/render.py new file mode 100755 index 0000000..975d6d7 --- /dev/null +++ b/tests/utils/render.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +import os + +# Get environment variables +client_id = os.getenv("ZPA_CLIENT_ID") +client_secret = os.getenv("ZPA_CLIENT_SECRET") +customer_id = os.getenv("ZPA_CUSTOMER_ID") + +content = """ +--- +client_id: %s +client_secret: %s +customer_id: %s + +""" % ( + client_id, + client_secret, + customer_id, +) + +f = open("./tests/integration/integration_config.yml", "w") +f.write(content) +f.close()