diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index f14a4f3..b346fc0 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -43,7 +43,7 @@ jobs: uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 - name: Run ansible-lint - uses: ansible/ansible-lint@95382d398ea1744bf6bfa47b030f14c38b3f6957 # v24.7.0 + uses: ansible/ansible-lint@v25.7.0 - name: Install detect-secrets run: pip install detect-secrets==1.4.0 diff --git a/plugins/module_utils/policy_templates/definition.py b/plugins/module_utils/policy_templates/definition.py index ad167dc..3608249 100644 --- a/plugins/module_utils/policy_templates/definition.py +++ b/plugins/module_utils/policy_templates/definition.py @@ -5,6 +5,7 @@ AclPolicy, AdvancedInspectionProfilePolicy, AdvancedMalwareProtectionPolicy, + AppRoutePolicy, CflowdPolicy, ControlPolicy, DeviceAccessIPv6Policy, @@ -31,6 +32,7 @@ "access_control_policy_ipv6": AclIPv6Policy, "aip": AdvancedInspectionProfilePolicy, "amp": AdvancedMalwareProtectionPolicy, + "app_route": AppRoutePolicy, "cflowd": CflowdPolicy, "control": ControlPolicy, "device_access": DeviceAccessPolicy, @@ -64,6 +66,7 @@ "default": "feature", }, "definition": {"type": "dict"}, + "sequences": {"type": "list"}, }, } } diff --git a/plugins/module_utils/policy_templates/security.py b/plugins/module_utils/policy_templates/security.py new file mode 100644 index 0000000..3fbb6ce --- /dev/null +++ b/plugins/module_utils/policy_templates/security.py @@ -0,0 +1,15 @@ +policy_security_definition = { + "security": { + "default": None, + "required": False, + "type": "dict", + "options": { + "type": { + "type": "str", + "choices": ["feature", "cli"], + "default": "feature", + }, + "definition": {"type": "dict"}, + }, + } +} diff --git a/plugins/modules/cli_templates_info.py b/plugins/modules/cli_templates_info.py index fce7dfe..54a65bd 100644 --- a/plugins/modules/cli_templates_info.py +++ b/plugins/modules/cli_templates_info.py @@ -126,8 +126,6 @@ type: list """ -from typing import Literal - from catalystwan.api.template_api import CLITemplate from catalystwan.api.templates.device_template.device_template import DeviceTemplate from catalystwan.models.templates import DeviceTemplateInformation diff --git a/plugins/modules/policy.py b/plugins/modules/policy.py index f561adf..5a13e12 100644 --- a/plugins/modules/policy.py +++ b/plugins/modules/policy.py @@ -110,10 +110,12 @@ from catalystwan.models.policy import ( AnyPolicyDefinition, AnyPolicyList, + AnySecurityPolicyInfo, CentralizedPolicy, CentralizedPolicyInfo, LocalizedPolicy, LocalizedPolicyInfo, + SecurityPolicy, ) from catalystwan.models.policy.centralized import CentralizedPolicyEditPayload from catalystwan.session import ManagerHTTPError @@ -123,6 +125,7 @@ from ..module_utils.policy_templates.definition import policy_definition_definition, policy_definition_type_mapping from ..module_utils.policy_templates.list import policy_list_definition, policy_list_type_mapping from ..module_utils.policy_templates.localized import policy_localized_definition +from ..module_utils.policy_templates.security import policy_security_definition from ..module_utils.result import ModuleResult from ..module_utils.vmanage_module import AnsibleCatalystwanModule @@ -157,6 +160,7 @@ def run_module(): **policy_definition_definition, **policy_centralized_definition, **policy_localized_definition, + **policy_security_definition, ) result = ModuleResult() @@ -169,6 +173,7 @@ def run_module(): "localized", "list", "definition", + "security", ), ], mutually_exclusive=[ @@ -177,6 +182,7 @@ def run_module(): "localized", "list", "definition", + "security", ), ], ) @@ -184,16 +190,36 @@ def run_module(): object_name: str = module.params.get("name") object_description: str = module.params.get("description") - if module.params.get("centralized"): - object_pretty_name = "Centralized Policy" - object_type = CentralizedPolicy - object_endpoint = module.session.api.policy.centralized - all_centralized_policy: DataSequence[CentralizedPolicyInfo] = module.get_response_safely(object_endpoint.get) - filtered_definitions: Optional[DataSequence[CentralizedPolicyInfo]] = all_centralized_policy.filter( - policy_name=object_name - ) + if module.params.get("centralized") or module.params.get("localized") or module.params.get("security"): + if module.params.get("centralized"): + object_pretty_name = "Centralized Policy" + object_type = CentralizedPolicy + object_edit_type = CentralizedPolicyEditPayload + object_endpoint = module.session.api.policy.centralized + policy_definition = module.params.get("centralized") + elif module.params.get("localized"): + object_pretty_name = "Localized Policy" + object_type = LocalizedPolicy + object_edit_type = object_type + object_endpoint = module.session.api.policy.localized + policy_definition = module.params.get("localized") + elif module.params.get("security"): + object_pretty_name = "Security Policy" + object_type = SecurityPolicy + object_edit_type = object_type + object_endpoint = module.session.api.policy.security + policy_definition = module.params.get("security") + + all_policies: DataSequence[ + CentralizedPolicyInfo | LocalizedPolicyInfo | AnySecurityPolicyInfo + ] = module.get_response_safely(object_endpoint.get) + if module.params.get("security"): + all_policies = all_policies.security + filtered_definitions: Optional[ + DataSequence[CentralizedPolicyInfo | LocalizedPolicyInfo | AnySecurityPolicyInfo] + ] = all_policies.filter(policy_name=object_name) if filtered_definitions: - existing_object: DataSequence[CentralizedPolicy] = [ + existing_object: DataSequence[CentralizedPolicy | LocalizedPolicy | SecurityPolicy] = [ module.get_response_safely(object_endpoint.get, id=filtered_definitions[0].policy_id) ] existing_object_id = filtered_definitions[0].policy_id @@ -204,52 +230,20 @@ def run_module(): object_to_create = object_type( policy_name=object_name, policy_description=object_description, - policy_type=module.params.get("centralized").get("type"), - policy_definition=module.params.get("centralized").get("definition"), + policy_type=policy_definition.get("type"), + policy_definition=policy_definition.get("definition"), is_policy_activated=module.params.get("state") == "active", ) elif module.params.get("state") in ("active", "present"): - object_to_create = CentralizedPolicyEditPayload( + object_to_create = object_edit_type( policy_name=object_name, policy_description=object_description, - policy_type=module.params.get("centralized").get("type"), - policy_definition=module.params.get("centralized").get("definition"), + policy_type=policy_definition.get("type"), + policy_definition=policy_definition.get("definition"), is_policy_activated=module.params.get("state") == "active", policy_id=existing_object_id, ) - elif module.params.get("localized"): - object_pretty_name = "Localized Policy" - object_type = LocalizedPolicy - object_endpoint = module.session.api.policy.localized - all_localized_policy: DataSequence[LocalizedPolicyInfo] = module.get_response_safely(object_endpoint.get) - filtered_definitions: Optional[DataSequence[LocalizedPolicyInfo]] = all_localized_policy.filter( - policy_name=object_name - ) - if filtered_definitions: - existing_object: DataSequence[LocalizedPolicy] = [ - module.get_response_safely(object_endpoint.get, id=filtered_definitions[0].policy_id) - ] - existing_object_id = filtered_definitions[0].policy_id - else: - existing_object = [] - - if module.params.get("state") in ("active", "present") and not existing_object: - object_to_create = object_type( - policy_name=object_name, - policy_description=object_description, - policy_type=module.params.get("localized").get("type"), - policy_definition=module.params.get("localized").get("definition"), - ) - elif module.params.get("state") in ("active", "present"): - object_to_create = LocalizedPolicy( - policy_name=object_name, - policy_description=object_description, - policy_type=module.params.get("localized").get("type"), - policy_definition=module.params.get("localized").get("definition"), - policy_id=existing_object_id, - ) - elif module.params.get("definition"): object_pretty_name = "Policy Definition" object_type = policy_definition_type_mapping[module.params.get("definition").get("type")] @@ -259,6 +253,7 @@ def run_module(): name=object_name, description=object_description, definition=module.params.get("definition").get("definition"), + sequences=module.params.get("definition").get("sequences"), ) all_policy_definitions: DataSequence[AnyPolicyDefinition] = module.get_response_safely( object_endpoint.get, type=object_type @@ -315,8 +310,8 @@ def run_module(): ) device_action.wait_for_completed() object_endpoint.edit(policy=object_to_create) - elif module.params.get("localized"): - object_endpoint.edit(policy=object_to_create) + elif module.params.get("localized") or module.params.get("security"): + object_endpoint.edit(id=existing_object_id, policy=object_to_create) elif module.params.get("definition"): object_endpoint.edit( id=existing_object_id, @@ -340,7 +335,7 @@ def run_module(): # object doesn't exist in Manager and needs to be created else: try: - if module.params.get("centralized") or module.params.get("localized"): + if module.params.get("centralized") or module.params.get("localized") or module.params.get("security"): created_uuid: UUID = module.get_response_safely(object_endpoint.create, policy=object_to_create) elif module.params.get("definition"): created_uuid: UUID = module.get_response_safely( diff --git a/roles/policies/README.md b/roles/policies/README.md new file mode 100644 index 0000000..483f004 --- /dev/null +++ b/roles/policies/README.md @@ -0,0 +1,73 @@ +# Ansible Role: policies + +This Ansible role provides user with a few, ready to use, legacy policies scenarios. It handles creating legacy policies along will all required policies definitions and lists. + +## Role Description + +The `policies` role performs the following tasks: + +1. Create legacy policies based on user provided configuration. Supported scenarios: + - hub and spoke topology + - mesh topology + - application route policy + - acl policy + - geolocation blockade + +## Requirements + +- `cisco.catalystwan` collection installed. +- Access details for the Cisco Manager instance must be provided. + +## Dependencies + +There are no external role dependencies. Only `cisco.catalystwan` collection is required. + +## Role Variables + +Variables expected by this role: + +- `vmanage_instances`: A list of vManage instances containing management IP, admin username, and admin password. +- `policies`: A dictionary containing configuration of policies + +Example of `vmanage_instances`: + +```yaml +vmanage_instances: + - hostname: 'vmanage01' + system_ip: '192.0.2.10' + mgmt_public_ip: '198.51.100.10' + admin_username: 'admin' + admin_password: 'password' +``` + +Example of `policies`: +```yaml +policies: + mesh: + - name: my_mesh_policy + vpns: + - 100 + - 101 + regions: + - name: mesh_region1 + sites: + - 100 + - name: mesh_region2 + sites: + - 101 + app_route: + - name: my_app_counter + match: + source_ip: 10.0.0.0/24 + destination_port: 64534 + action: + counter: my_counter +``` + +## License + +"GPL-3.0-only" + +## Author Information + +This role was created by Piotr Piwowarski diff --git a/roles/policies/defaults/main.yml b/roles/policies/defaults/main.yml new file mode 100644 index 0000000..403c7d5 --- /dev/null +++ b/roles/policies/defaults/main.yml @@ -0,0 +1,19 @@ +# Copyright 2025 Cisco Systems, Inc. and its affiliates +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +--- + +policies: {} +default_policy: + name: "Ansible Managed policy" + hub_and_spoke: [] + mesh: [] + acl_policy: [] + app_route: [] + geolocation_block: [] + +combined_policies: "{{ default_policy | combine(policies, recursive=True) }}" + +created_centralized_policies: [] +created_localized_policies: [] +created_security_policies: [] diff --git a/roles/policies/meta/main.yml b/roles/policies/meta/main.yml new file mode 100644 index 0000000..aa3c3b6 --- /dev/null +++ b/roles/policies/meta/main.yml @@ -0,0 +1,15 @@ +--- + +galaxy_info: + author: Piotr Piwowarski + description: Allow user to configure pre defined policies in Cisco SD-WAN + license: GPL-3.0-or-later + min_ansible_version: "2.16.6" + + galaxy_tags: + - cisco + - sdwan + - catalystwan + - networking + +dependencies: [] diff --git a/roles/policies/tasks/acl_policy.yml b/roles/policies/tasks/acl_policy.yml new file mode 100644 index 0000000..03c643d --- /dev/null +++ b/roles/policies/tasks/acl_policy.yml @@ -0,0 +1,51 @@ +# Copyright 2025 Cisco Systems, Inc. and its affiliates +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +--- +- name: "Create acl policy {{ policy_item['name'] }}" + cisco.catalystwan.policy: + name: "{{ policy_item['name'] }}" + definition: + type: "access_control_list" + sequences: "{{ _sequences | from_yaml }}" + manager_credentials: + url: "{{ (vmanage_instances | first).mgmt_public_ip }}" + username: "{{ (vmanage_instances | first).admin_username }}" + password: "{{ (vmanage_instances | first).admin_password }}" + register: result_policy + vars: + _sequences: | + - sequenceId: 1 + {% if "next_hop" in policy_item['action'] %} + actions: + - type: set + parameter: + - field: nextHop + value: "{{ policy_item['action']['next_hop'] }}" + {% endif %} + match: + entries: + {% if "source_port" in policy_item['match'] %} + - field: sourcePort + value: "{{ policy_item['match']['source_port'] }}" + {% endif %} + {% if "destination_port" in policy_item['match'] %} + - field: destinationPort + value: "{{ policy_item['match']['destination_port'] }}" + {% endif %} + {% if "source_ip" in policy_item['match'] %} + - field: sourceIp + value: "{{ policy_item['match']['source_ip'] }}" + {% endif %} + {% if "destination_ip" in policy_item['match'] %} + - field: destinationIp + value: "{{ policy_item['match']['destination_ip'] }}" + {% endif %} + +- name: Save policy id + ansible.builtin.set_fact: + created_localized_policies: "{{ created_localized_policies + [_created_policy] }}" + vars: + _created_policy: + type: acl + definitionId: "{{ result_policy['id'] }}" diff --git a/roles/policies/tasks/app_route.yml b/roles/policies/tasks/app_route.yml new file mode 100644 index 0000000..819f93f --- /dev/null +++ b/roles/policies/tasks/app_route.yml @@ -0,0 +1,73 @@ +# Copyright 2025 Cisco Systems, Inc. and its affiliates +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +--- +- name: "Create SLA class list for application route policy {{ policy_item['name'] }}" + cisco.catalystwan.policy: + name: "{{ policy_item['name'] }}_sla" + list: + type: "sla" + entries: + - loss: "{{ policy_item['action']['sla_class']['loss'] }}" + latency: "{{ policy_item['action']['sla_class']['latency'] }}" + jitter: "{{ policy_item['action']['sla_class']['jitter'] }}" + manager_credentials: + url: "{{ (vmanage_instances | first).mgmt_public_ip }}" + username: "{{ (vmanage_instances | first).admin_username }}" + password: "{{ (vmanage_instances | first).admin_password }}" + when: "'sla_class' in policy_item['action']" + register: result_sla_class + +- name: "Create application route policy {{ policy_item['name'] }}" + cisco.catalystwan.policy: + name: "{{ policy_item['name'] }}" + definition: + type: "app_route" + sequences: "{{ _sequences | from_yaml }}" + manager_credentials: + url: "{{ (vmanage_instances | first).mgmt_public_ip }}" + username: "{{ (vmanage_instances | first).admin_username }}" + password: "{{ (vmanage_instances | first).admin_password }}" + register: result_policy + vars: + _sequences: | + - sequenceId: 1 + actions: + {% if "counter" in policy_item['action'] %} + - type: count + parameter: "{{ policy_item['action']['counter'] }}" + {% endif %} + {% if "log" in policy_item['action'] %} + - type: log + parameter: "{{ policy_item['action']['log'] }}" + {% endif %} + {% if "log" in policy_item['action'] %} + - type: slaClass + parameter: "{{ policy_item['action']['sla_class'] }}" + {% endif %} + match: + entries: + {% if "source_port" in policy_item['match'] %} + - field: sourcePort + value: "{{ policy_item['match']['source_port'] }}" + {% endif %} + {% if "destination_port" in policy_item['match'] %} + - field: destinationPort + value: "{{ policy_item['match']['destination_port'] }}" + {% endif %} + {% if "source_ip" in policy_item['match'] %} + - field: sourceIp + value: "{{ policy_item['match']['source_ip'] }}" + {% endif %} + {% if "destination_ip" in policy_item['match'] %} + - field: destinationIp + value: "{{ policy_item['match']['destination_ip'] }}" + {% endif %} + +- name: Save policy id + ansible.builtin.set_fact: + created_centralized_policies: "{{ created_centralized_policies + [_created_policy] }}" + vars: + _created_policy: + type: appRoute + definitionId: "{{ result_policy['id'] }}" diff --git a/roles/policies/tasks/example-forwarding-qos.yml b/roles/policies/tasks/example-forwarding-qos.yml new file mode 100644 index 0000000..48201b9 --- /dev/null +++ b/roles/policies/tasks/example-forwarding-qos.yml @@ -0,0 +1,133 @@ +--- +# Copyright 2025 Cisco Systems, Inc. and its affiliates +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +# https://www.cisco.com/c/en/us/td/docs/routers/sdwan/configuration/qos/vEdge-20-x/qos-book/forwarding-qos.html#Cisco_Concept.dita_aa3e0d07-462e-463f-8f45-681f38f61ab0 + +- name: Save list of QoS schedulers + ansible.builtin.set_fact: + qos_schedulers: + - name: VOICE + queue: 0 + bandwidth: 10 + buffer: 10 + dscp: 0 + l2cos: 0 + - name: CRITICAL_DATA + queue: 1 + bandwidth: 30 + buffer: 30 + dscp: 2 + l2cos: 18 + - name: BULK + queue: 2 + bandwidth: 10 + buffer: 10 + dscp: 10 + l2cos: 1 + - name: DEFAULT + queue: 3 + bandwidth: 20 + buffer: 20 + dscp: 0 + l2cos: 0 + - name: INTERACTIVE_VIDEO + queue: 4 + bandwidth: 20 + buffer: 20 + dscp: 4 + l2cos: 10 + - name: CONTROL_SIGNALING + queue: 5 + bandwidth: 10 + buffer: 10 + dscp: 18 + l2cos: 2 + drops: "tail-drop" + +- name: Create ClassMap lists + cisco.catalystwan.policy: + name: "{{ item.name }}" + list: + type: "class_map" + entries: + - queue: "{{ item.queue }}" + manager_credentials: + url: "{{ (vmanage_instances | first).mgmt_public_ip }}" + username: "{{ (vmanage_instances | first).admin_username }}" + password: "{{ (vmanage_instances | first).admin_password }}" + register: created_class_map_lists + loop: "{{ qos_schedulers }}" + +- name: Prepare QoS schedulers for policy definition + ansible.builtin.set_fact: + policy_qos_schedulers: "{{ policy_qos_schedulers | default([]) + [qos_scheduler] }}" + loop: "{{ created_class_map_lists.results }}" + vars: + qos_scheduler: + queue: "{{ item.item.queue }}" + classMapRef: "{{ item.id | default(omit) }}" + bandwidthPercent: "{{ item.item.bandwidth | default(omit) }}" + bufferPercent: "{{ item.item.buffer | default(omit) }}" + drops: "{{ item.item.drops | default(omit) }}" + scheduling: "{{ item.item.scheduling | default(omit) }}" + +- name: Create QoS map policy definition + cisco.catalystwan.policy: + name: "My-QosMap-Policy" + definition: + type: qos_map + definition: + qosSchedulers: "{{ policy_qos_schedulers }}" + manager_credentials: + url: "{{ (vmanage_instances | first).mgmt_public_ip }}" + username: "{{ (vmanage_instances | first).admin_username }}" + password: "{{ (vmanage_instances | first).admin_password }}" + register: created_qos_map + +- name: Prepare rewrite rules for policy definition + ansible.builtin.set_fact: + policy_rewrite_rules: "{{ policy_rewrite_rules | default([]) + rewrite_rules }}" + loop: "{{ created_class_map_lists.results }}" + when: item.item.queue > 0 + vars: + rewrite_rules: + - class: "{{ item.id | default(omit) }}" + plp: low + dscp: "{{ item.item.dscp | default(omit) }}" + l2cos: "{{ item.item.l2cos | default(omit) }}" + - class: "{{ item.id | default(omit) }}" + plp: high + dscp: "{{ item.item.dscp | default(omit) }}" + l2cos: "{{ item.item.l2cos | default(omit) }}" + +- name: Create Rewrite policy definition + cisco.catalystwan.policy: + name: "My-Rewrite-Policy" + definition: + type: rewrite + definition: + rules: "{{ policy_rewrite_rules }}" + manager_credentials: + url: "{{ (vmanage_instances | first).mgmt_public_ip }}" + username: "{{ (vmanage_instances | first).admin_username }}" + password: "{{ (vmanage_instances | first).admin_password }}" + register: created_rewrite_rules + +- name: Create localized policy + cisco.catalystwan.policy: + name: "My-Localized-Policy" + localized: + definition: + settings: + cloudQos: true + cloud_qos_service_side: true + assembly: + - type: qosMap + definitionId: "{{ created_qos_map.id }}" + - type: rewriteRule + definitionId: "{{ created_rewrite_rules.id }}" + manager_credentials: + url: "{{ (vmanage_instances | first).mgmt_public_ip }}" + username: "{{ (vmanage_instances | first).admin_username }}" + password: "{{ (vmanage_instances | first).admin_password }}" diff --git a/roles/policies/tasks/geolocation_block.yml b/roles/policies/tasks/geolocation_block.yml new file mode 100644 index 0000000..ead6322 --- /dev/null +++ b/roles/policies/tasks/geolocation_block.yml @@ -0,0 +1,72 @@ +# Copyright 2025 Cisco Systems, Inc. and its affiliates +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +--- +- name: "Create source zone for geolocation block policy {{ policy_item['name'] }}" + cisco.catalystwan.policy: + name: "{{ policy_item['name'] }}_src" + list: + type: "zone" + entries: + - vpn: "{{ policy_item['source_vpn'] }}" + manager_credentials: + url: "{{ (vmanage_instances | first).mgmt_public_ip }}" + username: "{{ (vmanage_instances | first).admin_username }}" + password: "{{ (vmanage_instances | first).admin_password }}" + register: result_source_zone + +- name: "Create destination zone for geolocation block policy {{ policy_item['name'] }}" + cisco.catalystwan.policy: + name: "{{ policy_item['name'] }}_dst" + list: + type: "zone" + entries: + - vpn: "{{ policy_item['destination_vpn'] }}" + manager_credentials: + url: "{{ (vmanage_instances | first).mgmt_public_ip }}" + username: "{{ (vmanage_instances | first).admin_username }}" + password: "{{ (vmanage_instances | first).admin_password }}" + register: result_destination_zone + + +- name: "Create security policy {{ policy_item['name'] }}" + cisco.catalystwan.policy: + name: "{{ policy_item['name'] }}" + definition: + type: "zone_based_firewall" + definition: + entries: + - sourceZone: "{{ result_source_zone['id'] }}" + destinationZone: "{{ result_destination_zone['id'] }}" + sequences: "{{ _sequences | from_yaml }}" + manager_credentials: + url: "{{ (vmanage_instances | first).mgmt_public_ip }}" + username: "{{ (vmanage_instances | first).admin_username }}" + password: "{{ (vmanage_instances | first).admin_password }}" + register: result_policy + vars: + _sequences: | + - sequenceId: 1 + sequenceName: sequence 1 + match: + entries: + {% for geolocation in policy_item['geolocations'] %} + - field: sourceGeoLocation + value: "{{ geolocation }}" + {% endfor %} + - sequenceId: 2 + sequenceName: sequence 2 + match: + entries: + {% for geolocation in policy_item['geolocations'] %} + - field: destinationGeoLocation + value: "{{ geolocation }}" + {% endfor %} + +- name: Save policy id + ansible.builtin.set_fact: + created_security_policies: "{{ created_security_policies + [_created_policy] }}" + vars: + _created_policy: + type: zoneBasedFW + definitionId: "{{ result_policy['id'] }}" diff --git a/roles/policies/tasks/hub_and_spoke.yml b/roles/policies/tasks/hub_and_spoke.yml new file mode 100644 index 0000000..a72d1c9 --- /dev/null +++ b/roles/policies/tasks/hub_and_spoke.yml @@ -0,0 +1,80 @@ +# Copyright 2025 Cisco Systems, Inc. and its affiliates +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +--- +- name: "Create vpn list for hub and spoke policy {{ policy_item['name'] }}" + cisco.catalystwan.policy: + name: "{{ policy_item['name'] }}_vpn" + list: + type: "vpn" + entries: "{{ _entries | from_yaml }}" + manager_credentials: + url: "{{ (vmanage_instances | first).mgmt_public_ip }}" + username: "{{ (vmanage_instances | first).admin_username }}" + password: "{{ (vmanage_instances | first).admin_password }}" + register: result_vpn_list + vars: + _entries: | + {% for id in policy_item['vpns'] %} + - vpn: {{ id }} + {% endfor %} + +- name: "Create hub list for hub and spoke policy {{ policy_item['name'] }}" + cisco.catalystwan.policy: + name: "{{ policy_item['name'] }}_hub" + list: + type: "site" + entries: "{{ _entries | from_yaml }}" + manager_credentials: + url: "{{ (vmanage_instances | first).mgmt_public_ip }}" + username: "{{ (vmanage_instances | first).admin_username }}" + password: "{{ (vmanage_instances | first).admin_password }}" + register: result_hub_list + vars: + _entries: | + {% for id in policy_item['hubs'] %} + - site_id: "{{ id }}" + {% endfor %} + +- name: "Create spoke list for hub and spoke policy {{ policy_item['name'] }}" + cisco.catalystwan.policy: + name: "{{ policy_item['name'] }}_spoke" + list: + type: "site" + entries: "{{ _entries | from_yaml }}" + manager_credentials: + url: "{{ (vmanage_instances | first).mgmt_public_ip }}" + username: "{{ (vmanage_instances | first).admin_username }}" + password: "{{ (vmanage_instances | first).admin_password }}" + register: result_spoke_list + vars: + _entries: | + {% for id in policy_item['spokes'] %} + - site_id: "{{ id }}" + {% endfor %} + +- name: "Create hub and spoke policy {{ policy_item['name'] }}" + cisco.catalystwan.policy: + name: "{{ policy_item['name'] }}" + definition: + type: "hub_and_spoke" + definition: + vpnList: "{{ result_vpn_list['id'] }}" + subDefinitions: + - spokes: + - siteList: "{{ result_spoke_list['id'] }}" + hubs: + - siteList: "{{ result_spoke_list['id'] }}" + manager_credentials: + url: "{{ (vmanage_instances | first).mgmt_public_ip }}" + username: "{{ (vmanage_instances | first).admin_username }}" + password: "{{ (vmanage_instances | first).admin_password }}" + register: result_policy + +- name: Save policy id + ansible.builtin.set_fact: + created_centralized_policies: "{{ created_centralized_policies + [_created_policy] }}" + vars: + _created_policy: + type: hubAndSpoke + definitionId: "{{ result_policy['id'] }}" diff --git a/roles/policies/tasks/main.yml b/roles/policies/tasks/main.yml new file mode 100644 index 0000000..185693d --- /dev/null +++ b/roles/policies/tasks/main.yml @@ -0,0 +1,75 @@ +# Copyright 2025 Cisco Systems, Inc. and its affiliates +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +--- + +- name: Create hub and spoke policy + ansible.builtin.include_tasks: hub_and_spoke.yml + loop: "{{ combined_policies['hub_and_spoke'] }}" + loop_control: + loop_var: policy_item + label: "{{ policy_item['name'] }}" + +- name: Create mesh policy + ansible.builtin.include_tasks: mesh.yml + loop: "{{ combined_policies['mesh'] }}" + loop_control: + loop_var: policy_item + label: "{{ policy_item['name'] }}" + +- name: Create application route policy + ansible.builtin.include_tasks: app_route.yml + loop: "{{ combined_policies['app_route'] }}" + loop_control: + loop_var: policy_item + label: "{{ policy_item['name'] }}" + +- name: Create centralized policy + cisco.catalystwan.policy: + name: "ansible-managed-policy" + centralized: + definition: + assembly: "{{ created_centralized_policies }}" + manager_credentials: + url: "{{ (vmanage_instances | first).mgmt_public_ip }}" + username: "{{ (vmanage_instances | first).admin_username }}" + password: "{{ (vmanage_instances | first).admin_password }}" + when: created_centralized_policies + +- name: Create acl policy + ansible.builtin.include_tasks: acl_policy.yml + loop: "{{ combined_policies['acl_policy'] }}" + loop_control: + loop_var: policy_item + label: "{{ policy_item['name'] }}" + +- name: Create localized policy + cisco.catalystwan.policy: + name: "ansible-managed-policy" + localized: + definition: + assembly: "{{ created_localized_policies }}" + manager_credentials: + url: "{{ (vmanage_instances | first).mgmt_public_ip }}" + username: "{{ (vmanage_instances | first).admin_username }}" + password: "{{ (vmanage_instances | first).admin_password }}" + when: created_localized_policies + +- name: Create geolocation block policy + ansible.builtin.include_tasks: geolocation_block.yml + loop: "{{ combined_policies['geolocation_block'] }}" + loop_control: + loop_var: policy_item + label: "{{ policy_item['name'] }}" + +- name: Create security policy + cisco.catalystwan.policy: + name: "ansible-managed-policy" + security: + definition: + assembly: "{{ created_security_policies }}" + manager_credentials: + url: "{{ (vmanage_instances | first).mgmt_public_ip }}" + username: "{{ (vmanage_instances | first).admin_username }}" + password: "{{ (vmanage_instances | first).admin_password }}" + when: created_security_policies diff --git a/roles/policies/tasks/mesh.yml b/roles/policies/tasks/mesh.yml new file mode 100644 index 0000000..5e04b35 --- /dev/null +++ b/roles/policies/tasks/mesh.yml @@ -0,0 +1,70 @@ +# Copyright 2025 Cisco Systems, Inc. and its affiliates +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +--- +- name: "Create vpn list for mesh topology {{ policy_item['name'] }}" + cisco.catalystwan.policy: + name: "{{ policy_item['name'] }}_vpn" + list: + type: "vpn" + entries: "{{ _entries | from_yaml }}" + manager_credentials: + url: "{{ (vmanage_instances | first).mgmt_public_ip }}" + username: "{{ (vmanage_instances | first).admin_username }}" + password: "{{ (vmanage_instances | first).admin_password }}" + register: result_vpn_list + vars: + _entries: | + {% for id in policy_item['vpns'] %} + - vpn: {{ id }} + {% endfor %} + +- name: "Create site list for mesh topology {{ policy_item['name'] }}" + cisco.catalystwan.policy: + name: "{{ region_item['name'] }}" + list: + type: "site" + entries: "{{ _entries | from_yaml }}" + manager_credentials: + url: "{{ (vmanage_instances | first).mgmt_public_ip }}" + username: "{{ (vmanage_instances | first).admin_username }}" + password: "{{ (vmanage_instances | first).admin_password }}" + loop: "{{ policy_item['regions'] }}" + loop_control: + loop_var: region_item + label: "{{ region_item['name'] }}" + register: result_region_list + vars: + _entries: | + {% for id in region_item['sites'] %} + - site_id: "{{ id }}" + {% endfor %} + +- name: "Create mesh policy {{ policy_item['name'] }}" + cisco.catalystwan.policy: + name: "{{ policy_item['name'] }}" + definition: + type: "mesh" + definition: + vpnList: "{{ result_vpn_list['id'] }}" + regions: "{{ _regions | from_yaml }}" + manager_credentials: + url: "{{ (vmanage_instances | first).mgmt_public_ip }}" + username: "{{ (vmanage_instances | first).admin_username }}" + password: "{{ (vmanage_instances | first).admin_password }}" + register: result_policy + vars: + _regions: | + {% for region in result_region_list['results'] %} + - name: "{{ region['region_item']['name'] }}" + siteLists: + - {{ region['id'] }} + {% endfor %} + +- name: Save policy id + ansible.builtin.set_fact: + created_centralized_policies: "{{ created_centralized_policies + [_created_policy] }}" + vars: + _created_policy: + type: mesh + definitionId: "{{ result_policy['id'] }}"