fmgr_fwpol_package¶
Metadata¶
Name: fmgr_fwpol_package
Description: Manages FortiManager Firewall Policies Packages. Policy Packages contain one or more Firewall Policies/Rules and are distritbuted via FortiManager to Fortigates. This module controls the creation/edit/delete/assign of these packages.
Author(s):
- Luke Weighall (github: @lweighall)
- Andrew Welsh (github: @Ghilli3)
- Jim Huber (github: @p4r4n0y1ng)
Ansible Version Added/Required: 2.8
Dev Status: COMPLETED/MERGED
Owning Developer: Luke Weighall
Module Github Link
Parameters¶
adom¶
- Description: The ADOM the configuration should belong to.
- Required: False
- default: root
central_nat¶
- Description: Central NAT setting.
- Required: False
- default: disable
- choices: [‘enable’, ‘disable’]
fwpolicy6_implicit_log¶
- Description: Implicit Log setting for all IPv6 policies in package.
- Required: False
- default: disable
- choices: [‘enable’, ‘disable’]
fwpolicy_implicit_log¶
- Description: Implicit Log setting for all IPv4 policies in package.
- Required: False
- default: disable
- choices: [‘enable’, ‘disable’]
inspection_mode¶
- Description: Inspection mode setting for the policies flow or proxy.
- Required: False
- default: flow
- choices: [‘flow’, ‘proxy’]
mode¶
Description: Set will overwrite existing installation targets with scope members.
Add will append existing installation targets with scope members.
Delete will delete the entire named package.
Add Targets will only add the specified scope members to installation targets.
Delete Targets will only delete the specified scope members from installation targets.
Install will install the package to the assigned installation targets listed on existing package.
Update your installation targets BEFORE running install task.
default: add
choices: [‘add’, ‘set’, ‘delete’, ‘move’, ‘copy’, ‘add_targets’, ‘delete_targets’, ‘install’]
name¶
- Description: Name of the FortiManager package or folder.
- Required: True
ngfw_mode¶
- Description: NGFW mode setting for the policies flow or proxy.
- Required: False
- default: profile-based
- choices: [‘profile-based’, ‘policy-based’]
object_type¶
- Description: Are we managing packages or package folders?
- Required: True
- choices: [‘pkg’, ‘folder’]
parent_folder¶
Description: The parent folder name you want to add this object under.
Nested folders are supported with forwardslashes. i.e. ansibleTestFolder1/ansibleTestFolder2/etc…
Do not include leading or trailing forwardslashes. We take care of that for you.
Required: False
scope_groups¶
- Description: List of groups to add to the scope of the fw pol package
- Required: False
scope_members¶
- Description: The devices or scope that you want to assign this policy package to. Only assign to one VDOM at a time.
- Required: False
scope_members_vdom¶
- Description: The members VDOM you want to assign the package to. Only assign to one VDOM at a time.
- Required: False
- default: root
ssl_ssh_profile¶
- Description: if policy-based ngfw-mode, refer to firewall ssl-ssh-profile.
- Required: False
target_folder¶
Description: Only used when mode equals move.
Nested folders are supported with forwardslashes. i.e. ansibleTestFolder1/ansibleTestFolder2/etc…
Do not include leading or trailing forwardslashes. We take care of that for you.
Required: False
target_name¶
Description: Only used when mode equals move.
Only used when you want to rename the package in its new location.
If None, then NAME will be used.
Required: False
Functions¶
- fmgr_fwpol_package
def fmgr_fwpol_package(fmgr, paramgram): """ This function will create FMGR Firewall Policy Packages, or delete them. It is also capable of assigning packages. This function DOES NOT install the package. See the function fmgr_fwpol_package_install() :param fmgr: The fmgr object instance from fmgr_utils.py :type fmgr: class object :param paramgram: The formatted dictionary of options to process :type paramgram: dict :return: The response from the FortiManager :rtype: dict """ if paramgram["mode"] in ['set', 'add']: url = '/pm/pkg/adom/{adom}'.format(adom=paramgram["adom"]) datagram = { "type": paramgram["object_type"], "name": paramgram["name"], "package settings": { "central-nat": paramgram["central-nat"], "fwpolicy-implicit-log": paramgram["fwpolicy-implicit-log"], "fwpolicy6-implicit-log": paramgram["fwpolicy6-implicit-log"], "inspection-mode": paramgram["inspection-mode"], "ngfw-mode": paramgram["ngfw-mode"], } } if paramgram["ngfw-mode"] == "policy-based" and paramgram["ssl-ssh-profile"] is not None: datagram["package settings"]["ssl-ssh-profile"] = paramgram["ssl-ssh-profile"] # SET THE SCOPE MEMBERS ACCORDING TO MODE AND WHAT WAS SUPPLIED if len(paramgram["append_members_list"]) > 0: datagram["scope member"] = paramgram["append_members_list"] elif len(paramgram["append_members_list"]) == 0: datagram["scope member"] = {} # IF PARENT FOLDER IS DEFINED if paramgram["parent_folder"] is not None: datagram = fmgr_fwpol_package_create_parent_folder_objects(paramgram, datagram) # IF MODE IS MOVE if paramgram['mode'] in ["move", "copy"]: # pydevd.settrace('10.0.0.151', port=54654, stdoutToServer=True, stderrToServer=True) if paramgram["mode"] == "move": url = '/securityconsole/package/move' elif paramgram["mode"] == "copy": url = '/securityconsole/package/clone' if paramgram["target_name"]: name = paramgram["target_name"] else: name = paramgram["name"] datagram = { "adom": paramgram["adom"], "dst_name": name, "dst_parent": paramgram["target_folder"], "pkg": paramgram["name"] } if paramgram["parent_folder"]: datagram["pkg"] = str(paramgram["parent_folder"]) + "/" + str(paramgram["name"]) else: datagram["pkg"] = str(paramgram["name"]) if paramgram["mode"] == "copy": # SET THE SCOPE MEMBERS ACCORDING TO MODE AND WHAT WAS SUPPLIED if len(paramgram["append_members_list"]) > 0: datagram["scope member"] = paramgram["append_members_list"] elif len(paramgram["append_members_list"]) == 0: datagram["scope member"] = {} # NORMAL DELETE NO PARENT if paramgram["mode"] == "delete" and paramgram["parent_folder"] is None: datagram = { "name": paramgram["name"] } # SET DELETE URL url = '/pm/pkg/adom/{adom}/{name}'.format(adom=paramgram["adom"], name=paramgram["name"]) # DELETE WITH PARENT if paramgram["mode"] == "delete" and paramgram["parent_folder"] is not None: datagram = { "name": paramgram["name"] } # SET DELETE URL url = '/pm/pkg/adom/{adom}/{parent_folder}/{name}'.format(adom=paramgram["adom"], name=paramgram["name"], parent_folder=paramgram["parent_folder"]) if paramgram['mode'] in ["move", "copy"]: response = fmgr.process_request(url, datagram, FMGRMethods.EXEC) else: response = fmgr.process_request(url, datagram, paramgram["mode"]) return response
- fmgr_fwpol_package_edit_targets
def fmgr_fwpol_package_edit_targets(fmgr, paramgram): """ This function will append scope targets to an existing policy package. :param fmgr: The fmgr object instance from fmgr_utils.py :type fmgr: class object :param paramgram: The formatted dictionary of options to process :type paramgram: dict :return: The response from the FortiManager :rtype: dict """ # MERGE APPEND AND EXISTING MEMBERS LISTS BASED ON MODE method = None members_list = None if paramgram["mode"] == "add_targets": method = FMGRMethods.ADD members_list = paramgram["append_members_list"] for member in paramgram["existing_members_list"]: if member not in members_list: members_list.append(member) elif paramgram["mode"] == "delete_targets": method = FMGRMethods.DELETE members_list = list() for member in paramgram["append_members_list"]: if member in paramgram["existing_members_list"]: members_list.append(member) datagram = { "data": members_list } if paramgram["parent_folder"] is not None: url = '/pm/pkg/adom/{adom}/{parent_folder}/{name}/scope member'.format(adom=paramgram["adom"], name=paramgram["name"], parent_folder=paramgram["parent_folder"]) elif paramgram["parent_folder"] is None: url = '/pm/pkg/adom/{adom}/{name}/scope member'.format(adom=paramgram["adom"], name=paramgram["name"]) response = fmgr.process_request(url, datagram, method) return response
- fmgr_fwpol_package_folder
def fmgr_fwpol_package_folder(fmgr, paramgram): """ This function will create folders for firewall packages. It can create down to two levels deep. We haven't yet tested for any more layers below two levels. parent_folders for multiple levels may need to defined as "level1/level2/level3" for the URL parameters and such. :param fmgr: The fmgr object instance from fmgr_utils.py :type fmgr: class object :param paramgram: The formatted dictionary of options to process :type paramgram: dict :return: The response from the FortiManager :rtype: dict """ if paramgram["mode"] in ['set', 'add']: url = '/pm/pkg/adom/{adom}'.format(adom=paramgram["adom"]) datagram = { "type": paramgram["object_type"], "name": paramgram["name"], } # IF PARENT FOLDER IS DEFINED if paramgram["parent_folder"] is not None: datagram = fmgr_fwpol_package_create_parent_folder_objects(paramgram, datagram) # NORMAL DELETE NO PARENT if paramgram["mode"] == "delete" and paramgram["parent_folder"] is None: datagram = { "name": paramgram["name"] } # SET DELETE URL url = '/pm/pkg/adom/{adom}/{name}'.format(adom=paramgram["adom"], name=paramgram["name"]) # DELETE WITH PARENT if paramgram["mode"] == "delete" and paramgram["parent_folder"] is not None: datagram = { "name": paramgram["name"] } # SET DELETE URL url = '/pm/pkg/adom/{adom}/{parent_folder}/{name}'.format(adom=paramgram["adom"], name=paramgram["name"], parent_folder=paramgram["parent_folder"]) response = fmgr.process_request(url, datagram, paramgram["mode"]) return response
- fmgr_fwpol_package_install
def fmgr_fwpol_package_install(fmgr, paramgram): """ This method/function installs FMGR FW Policy Packages to the scope members defined in the playbook. :param fmgr: The fmgr object instance from fmgr_utils.py :type fmgr: class object :param paramgram: The formatted dictionary of options to process :type paramgram: dict :return: The response from the FortiManager :rtype: dict """ datagram = { "adom": paramgram["adom"], "pkg": paramgram["name"], } if paramgram["parent_folder"]: new_path = str(paramgram["parent_folder"]) + "/" + str(paramgram["name"]) datagram["pkg"] = new_path # EXECUTE THE INSTALL REQUEST url = '/securityconsole/install/package' response = fmgr.process_request(url, datagram, FMGRMethods.EXEC) return response
- fmgr_fwpol_package_get_details
def fmgr_fwpol_package_get_details(fmgr, paramgram): """ This method/function will attempt to get existing package details, and append findings to the paramgram. If nothing is found, the paramgram additions are simply empty. :param fmgr: The fmgr object instance from fmgr_utils.py :type fmgr: class object :param paramgram: The formatted dictionary of options to process :type paramgram: dict :return: The response from the FortiManager :rtype: dict """ # CHECK FOR SCOPE MEMBERS AND CREATE THAT MEMBERS LIST # WE MUST PROPERLY FORMAT THE JSON FOR SCOPE MEMBERS WITH VDOMS members_list = list() if paramgram["scope_members"] is not None and paramgram["mode"] in ['add', 'set', 'add_targets', 'delete_targets']: if isinstance(paramgram["scope_members"], list): members = paramgram["scope_members"] if isinstance(paramgram["scope_members"], str): members = FMGRCommon.split_comma_strings_into_lists(paramgram["scope_members"]) for member in members: scope_dict = { "name": member, "vdom": paramgram["scope_members_vdom"], } members_list.append(scope_dict) # CHECK FOR SCOPE GROUPS AND ADD THAT TO THE MEMBERS LIST # WE MUST PROPERLY FORMAT THE JSON FOR SCOPE GROUPS if paramgram["scope_groups"] is not None and paramgram["mode"] in ['add', 'set', 'add_targets', 'delete_targets']: if isinstance(paramgram["scope_groups"], list): members = paramgram["scope_groups"] if isinstance(paramgram["scope_groups"], str): members = FMGRCommon.split_comma_strings_into_lists(paramgram["scope_groups"]) for member in members: scope_dict = { "name": member } members_list.append(scope_dict) # CHECK FOR AN EXISTING POLICY PACKAGE, AND GET ITS MEMBERS SO WE DON'T OVERWRITE THEM WITH NOTHING pol_datagram = {"type": paramgram["object_type"], "name": paramgram["name"]} if paramgram["parent_folder"]: pol_package_url = '/pm/pkg/adom/{adom}/{folder}/{pkg_name}'.format(adom=paramgram["adom"], pkg_name=paramgram["name"], folder=paramgram["parent_folder"]) else: pol_package_url = '/pm/pkg/adom/{adom}/{pkg_name}'.format(adom=paramgram["adom"], pkg_name=paramgram["name"]) pol_package = fmgr.process_request(pol_package_url, pol_datagram, FMGRMethods.GET) existing_members = None package_exists = None if len(pol_package) == 2: package_exists = True try: existing_members = pol_package[1]["scope member"] except Exception as err: existing_members = list() else: package_exists = False # ADD COLLECTED DATA TO PARAMGRAM FOR USE IN METHODS paramgram["existing_members_list"] = existing_members paramgram["append_members_list"] = members_list paramgram["package_exists"] = package_exists return paramgram
- fmgr_fwpol_package_create_parent_folder_objects
def fmgr_fwpol_package_create_parent_folder_objects(paramgram, datagram): """ This function/method will take a paramgram with parent folders defined, and create the proper structure so that objects are nested correctly. :param paramgram: The paramgram used :type paramgram: dict :param datagram: The datagram, so far, as created by another function. :type datagram: dict :return: new_datagram """ # SPLIT THE PARENT FOLDER INTO A LIST BASED ON FORWARD SLASHES # FORM THE DATAGRAM USING TEMPLATE ABOVE WITH THE PACKAGE NESTED IN A SUBOBJ subobj_list = list() subobj_list.append(datagram) new_datagram = { "type": "folder", "name": paramgram["parent_folder"], "subobj": subobj_list } parent_folders = paramgram["parent_folder"].split("/") # LOOP THROUGH PARENT FOLDERS AND ADD AS MANY SUB OBJECT NESTED DICTS AS REQUIRED # WE'RE BUILDING THE SUBOBJ NESTED OBJECT "INSIDE OUT" num_of_parents = len(parent_folders) if num_of_parents > 1: parent_list_position = num_of_parents - 1 # REPLACE THE EXISTING PARENT FOLDER STRING WITH SLASHES, WITH THE BOTTOM MOST NESTED FOLDER new_datagram["name"] = parent_folders[parent_list_position] parent_list_position -= 1 while parent_list_position >= 0: new_subobj_list = list() new_subobj_list.append(new_datagram) new_datagram = { "type": "folder", "name": parent_folders[parent_list_position], "subobj": new_subobj_list } parent_list_position -= 1 # SET DATAGRAM TO THE NEWLY NESTED DATAGRAM return new_datagram
- main
def main(): argument_spec = dict( adom=dict(required=False, type="str", default="root"), mode=dict(choices=["add", "set", "delete", "move", "copy", "add_targets", "delete_targets", "install"], type="str", default="add"), name=dict(required=False, type="str"), object_type=dict(required=True, type="str", choices=['pkg', 'folder']), central_nat=dict(required=False, type="str", default="disable", choices=['enable', 'disable']), fwpolicy_implicit_log=dict(required=False, type="str", default="disable", choices=['enable', 'disable']), fwpolicy6_implicit_log=dict(required=False, type="str", default="disable", choices=['enable', 'disable']), inspection_mode=dict(required=False, type="str", default="flow", choices=['flow', 'proxy']), ngfw_mode=dict(required=False, type="str", default="profile-based", choices=['profile-based', 'policy-based']), ssl_ssh_profile=dict(required=False, type="str"), scope_groups=dict(required=False, type="str"), scope_members=dict(required=False, type="str"), scope_members_vdom=dict(required=False, type="str", default="root"), parent_folder=dict(required=False, type="str"), target_folder=dict(required=False, type="str"), target_name=dict(required=False, type="str"), ) module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False,) # MODULE DATAGRAM paramgram = { "adom": module.params["adom"], "name": module.params["name"], "mode": module.params["mode"], "object_type": module.params["object_type"], "central-nat": module.params["central_nat"], "fwpolicy-implicit-log": module.params["fwpolicy_implicit_log"], "fwpolicy6-implicit-log": module.params["fwpolicy6_implicit_log"], "inspection-mode": module.params["inspection_mode"], "ngfw-mode": module.params["ngfw_mode"], "ssl-ssh-profile": module.params["ssl_ssh_profile"], "scope_groups": module.params["scope_groups"], "scope_members": module.params["scope_members"], "scope_members_vdom": module.params["scope_members_vdom"], "parent_folder": module.params["parent_folder"], "target_folder": module.params["target_folder"], "target_name": module.params["target_name"], "append_members_list": list(), "existing_members_list": list(), "package_exists": None, } module.paramgram = paramgram fmgr = None if module._socket_path: connection = Connection(module._socket_path) fmgr = FortiManagerHandler(connection, module) fmgr.tools = FMGRCommon() else: module.fail_json(**FAIL_SOCKET_MSG) # BEGIN MODULE-SPECIFIC LOGIC -- THINGS NEED TO HAPPEN DEPENDING ON THE ENDPOINT AND OPERATION results = DEFAULT_RESULT_OBJ # QUERY FORTIMANAGER FOR EXISTING PACKAGE DETAILS AND UPDATE PARAMGRAM paramgram = fmgr_fwpol_package_get_details(fmgr, paramgram) try: if paramgram["object_type"] == "pkg" and paramgram["mode"] in ["add", "set", "delete", "move", "copy"]: results = fmgr_fwpol_package(fmgr, paramgram) fmgr.govern_response(module=module, results=results, ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) except Exception as err: raise FMGBaseException(err) try: if paramgram["object_type"] == "pkg" and paramgram["package_exists"] \ and len(paramgram["append_members_list"]) > 0 \ and paramgram["mode"] in ['add_targets', 'delete_targets']: results = fmgr_fwpol_package_edit_targets(fmgr, paramgram) fmgr.govern_response(module=module, results=results, ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) except Exception as err: raise FMGBaseException(err) try: # IF THE object_type IS FOLDER LETS RUN THAT METHOD if paramgram["object_type"] == "folder": results = fmgr_fwpol_package_folder(fmgr, paramgram) fmgr.govern_response(module=module, results=results, ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) except Exception as err: raise FMGBaseException(err) try: # IF THE object_type IS INSTALL AND NEEDED PARAMETERS ARE DEFINED INSTALL THE PACKAGE if paramgram["name"] is not None and paramgram["object_type"] == "pkg" and paramgram["mode"] == "install": results = fmgr_fwpol_package_install(fmgr, paramgram) fmgr.govern_response(module=module, results=results, ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) except Exception as err: raise FMGBaseException(err) return module.exit_json(**results[1])
Module Source Code¶
#!/usr/bin/python
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.1",
"status": ["preview"],
"supported_by": "community"
}
DOCUMENTATION = '''
---
module: fmgr_fwpol_package
version_added: "2.8"
notes:
- Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/).
- Revision Comments April 2nd 2019
- Couldn't append to installation target list, only send a complete list. We've added modes for adding and
deleting targets for policy packages.
- Install mode has been added. Scope_members is no longer taken into account when mode = install.
Only the existing installation targets on the package will be used. Update installation targets before.
- Nested folders and packages now work properly. Before they were not.
- When using modes "add" or "set" with object_type = "pkg" the installation targets are STILL OVERWRITTEN with
what was supplied under scope_members and scope_groups. Use the add_targets or delete_targets mode first.
- When using "add_targets" or "delete_targets" for changing installation targets, only scope_members or
scope_groups is considered for changes to the package. To edit the package settings themselves, use "set".
- Revision Comments May 21st 2019
- Added support to move packages.
author:
- Luke Weighall (@lweighall)
- Andrew Welsh (@Ghilli3)
- Jim Huber (@p4r4n0y1ng)
short_description: Manages FortiManager Firewall Policies Packages.
description:
- Manages FortiManager Firewall Policies Packages. Policy Packages contain one or more Firewall Policies/Rules and
are distritbuted via FortiManager to Fortigates.
- This module controls the creation/edit/delete/assign of these packages.
options:
adom:
description:
- The ADOM the configuration should belong to.
required: false
default: root
mode:
description:
- Set will overwrite existing installation targets with scope members.
- Add will append existing installation targets with scope members.
- Delete will delete the entire named package.
- Add Targets will only add the specified scope members to installation targets.
- Delete Targets will only delete the specified scope members from installation targets.
- Install will install the package to the assigned installation targets listed on existing package.
- Update your installation targets BEFORE running install task.
choices: ['add', 'set', 'delete', 'move', 'copy', 'add_targets', 'delete_targets', 'install']
default: add
name:
description:
- Name of the FortiManager package or folder.
required: True
object_type:
description:
- Are we managing packages or package folders?
required: True
choices: ['pkg','folder']
central_nat:
description:
- Central NAT setting.
required: false
choices: ['enable', 'disable']
default: disable
fwpolicy_implicit_log:
description:
- Implicit Log setting for all IPv4 policies in package.
required: false
choices: ['enable', 'disable']
default: disable
fwpolicy6_implicit_log:
description:
- Implicit Log setting for all IPv6 policies in package.
required: false
choices: ['enable', 'disable']
default: disable
inspection_mode:
description:
- Inspection mode setting for the policies flow or proxy.
required: false
choices: ['flow', 'proxy']
default: flow
ngfw_mode:
description:
- NGFW mode setting for the policies flow or proxy.
required: false
choices: ['profile-based', 'policy-based']
default: profile-based
ssl_ssh_profile:
description:
- if policy-based ngfw-mode, refer to firewall ssl-ssh-profile.
required: false
scope_groups:
description:
- List of groups to add to the scope of the fw pol package
required: false
scope_members:
description:
- The devices or scope that you want to assign this policy package to. Only assign to one VDOM at a time.
required: false
scope_members_vdom:
description:
- The members VDOM you want to assign the package to. Only assign to one VDOM at a time.
required: false
default: root
parent_folder:
description:
- The parent folder name you want to add this object under.
- Nested folders are supported with forwardslashes. i.e. ansibleTestFolder1/ansibleTestFolder2/etc...
- Do not include leading or trailing forwardslashes. We take care of that for you.
required: false
target_folder:
description:
- Only used when mode equals move.
- Nested folders are supported with forwardslashes. i.e. ansibleTestFolder1/ansibleTestFolder2/etc...
- Do not include leading or trailing forwardslashes. We take care of that for you.
required: false
target_name:
description:
- Only used when mode equals move.
- Only used when you want to rename the package in its new location.
- If None, then NAME will be used.
required: false
'''
EXAMPLES = '''
- name: CREATE BASIC POLICY PACKAGE
fmgr_fwpol_package:
adom: "ansible"
mode: "add"
name: "testPackage"
object_type: "pkg"
- name: ADD PACKAGE WITH TARGETS
fmgr_fwpol_package:
mode: "add"
adom: "ansible"
name: "ansibleTestPackage1"
object_type: "pkg"
inspection_mode: "flow"
ngfw_mode: "profile-based"
scope_members: "seattle-fgt02, seattle-fgt03"
- name: ADD FOLDER
fmgr_fwpol_package:
mode: "add"
adom: "ansible"
name: "ansibleTestFolder1"
object_type: "folder"
- name: ADD PACKAGE INTO PARENT FOLDER
fmgr_fwpol_package:
mode: "set"
adom: "ansible"
name: "ansibleTestPackage2"
object_type: "pkg"
parent_folder: "ansibleTestFolder1"
- name: ADD FOLDER INTO PARENT FOLDER
fmgr_fwpol_package:
mode: "set"
adom: "ansible"
name: "ansibleTestFolder2"
object_type: "folder"
parent_folder: "ansibleTestFolder1"
- name: INSTALL PACKAGE
fmgr_fwpol_package:
mode: "install"
adom: "ansible"
name: "ansibleTestPackage1"
- name: REMOVE PACKAGE
fmgr_fwpol_package:
mode: "delete"
adom: "ansible"
name: "ansibleTestPackage1"
object_type: "pkg"
- name: REMOVE NESTED PACKAGE
fmgr_fwpol_package:
mode: "delete"
adom: "ansible"
name: "ansibleTestPackage2"
object_type: "pkg"
parent_folder: "ansibleTestFolder1"
- name: REMOVE NESTED FOLDER
fmgr_fwpol_package:
mode: "delete"
adom: "ansible"
name: "ansibleTestFolder2"
object_type: "folder"
parent_folder: "ansibleTestFolder1"
- name: REMOVE FOLDER
fmgr_fwpol_package:
mode: "delete"
adom: "ansible"
name: "ansibleTestFolder1"
object_type: "folder"
'''
RETURN = """
api_result:
description: full API response, includes status code and message
returned: always
type: str
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import Connection
from ansible.module_utils.network.fortimanager.fortimanager import FortiManagerHandler
from ansible.module_utils.network.fortimanager.common import FMGBaseException
from ansible.module_utils.network.fortimanager.common import FMGRCommon
from ansible.module_utils.network.fortimanager.common import DEFAULT_RESULT_OBJ
from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG
from ansible.module_utils.network.fortimanager.common import FMGRMethods
import pydevd
def fmgr_fwpol_package(fmgr, paramgram):
"""
This function will create FMGR Firewall Policy Packages, or delete them. It is also capable of assigning packages.
This function DOES NOT install the package. See the function fmgr_fwpol_package_install()
:param fmgr: The fmgr object instance from fmgr_utils.py
:type fmgr: class object
:param paramgram: The formatted dictionary of options to process
:type paramgram: dict
:return: The response from the FortiManager
:rtype: dict
"""
if paramgram["mode"] in ['set', 'add']:
url = '/pm/pkg/adom/{adom}'.format(adom=paramgram["adom"])
datagram = {
"type": paramgram["object_type"],
"name": paramgram["name"],
"package settings": {
"central-nat": paramgram["central-nat"],
"fwpolicy-implicit-log": paramgram["fwpolicy-implicit-log"],
"fwpolicy6-implicit-log": paramgram["fwpolicy6-implicit-log"],
"inspection-mode": paramgram["inspection-mode"],
"ngfw-mode": paramgram["ngfw-mode"],
}
}
if paramgram["ngfw-mode"] == "policy-based" and paramgram["ssl-ssh-profile"] is not None:
datagram["package settings"]["ssl-ssh-profile"] = paramgram["ssl-ssh-profile"]
# SET THE SCOPE MEMBERS ACCORDING TO MODE AND WHAT WAS SUPPLIED
if len(paramgram["append_members_list"]) > 0:
datagram["scope member"] = paramgram["append_members_list"]
elif len(paramgram["append_members_list"]) == 0:
datagram["scope member"] = {}
# IF PARENT FOLDER IS DEFINED
if paramgram["parent_folder"] is not None:
datagram = fmgr_fwpol_package_create_parent_folder_objects(paramgram, datagram)
# IF MODE IS MOVE
if paramgram['mode'] in ["move", "copy"]:
# pydevd.settrace('10.0.0.151', port=54654, stdoutToServer=True, stderrToServer=True)
if paramgram["mode"] == "move":
url = '/securityconsole/package/move'
elif paramgram["mode"] == "copy":
url = '/securityconsole/package/clone'
if paramgram["target_name"]:
name = paramgram["target_name"]
else:
name = paramgram["name"]
datagram = {
"adom": paramgram["adom"],
"dst_name": name,
"dst_parent": paramgram["target_folder"],
"pkg": paramgram["name"]
}
if paramgram["parent_folder"]:
datagram["pkg"] = str(paramgram["parent_folder"]) + "/" + str(paramgram["name"])
else:
datagram["pkg"] = str(paramgram["name"])
if paramgram["mode"] == "copy":
# SET THE SCOPE MEMBERS ACCORDING TO MODE AND WHAT WAS SUPPLIED
if len(paramgram["append_members_list"]) > 0:
datagram["scope member"] = paramgram["append_members_list"]
elif len(paramgram["append_members_list"]) == 0:
datagram["scope member"] = {}
# NORMAL DELETE NO PARENT
if paramgram["mode"] == "delete" and paramgram["parent_folder"] is None:
datagram = {
"name": paramgram["name"]
}
# SET DELETE URL
url = '/pm/pkg/adom/{adom}/{name}'.format(adom=paramgram["adom"], name=paramgram["name"])
# DELETE WITH PARENT
if paramgram["mode"] == "delete" and paramgram["parent_folder"] is not None:
datagram = {
"name": paramgram["name"]
}
# SET DELETE URL
url = '/pm/pkg/adom/{adom}/{parent_folder}/{name}'.format(adom=paramgram["adom"],
name=paramgram["name"],
parent_folder=paramgram["parent_folder"])
if paramgram['mode'] in ["move", "copy"]:
response = fmgr.process_request(url, datagram, FMGRMethods.EXEC)
else:
response = fmgr.process_request(url, datagram, paramgram["mode"])
return response
def fmgr_fwpol_package_edit_targets(fmgr, paramgram):
"""
This function will append scope targets to an existing policy package.
:param fmgr: The fmgr object instance from fmgr_utils.py
:type fmgr: class object
:param paramgram: The formatted dictionary of options to process
:type paramgram: dict
:return: The response from the FortiManager
:rtype: dict
"""
# MERGE APPEND AND EXISTING MEMBERS LISTS BASED ON MODE
method = None
members_list = None
if paramgram["mode"] == "add_targets":
method = FMGRMethods.ADD
members_list = paramgram["append_members_list"]
for member in paramgram["existing_members_list"]:
if member not in members_list:
members_list.append(member)
elif paramgram["mode"] == "delete_targets":
method = FMGRMethods.DELETE
members_list = list()
for member in paramgram["append_members_list"]:
if member in paramgram["existing_members_list"]:
members_list.append(member)
datagram = {
"data": members_list
}
if paramgram["parent_folder"] is not None:
url = '/pm/pkg/adom/{adom}/{parent_folder}/{name}/scope member'.format(adom=paramgram["adom"],
name=paramgram["name"],
parent_folder=paramgram["parent_folder"])
elif paramgram["parent_folder"] is None:
url = '/pm/pkg/adom/{adom}/{name}/scope member'.format(adom=paramgram["adom"],
name=paramgram["name"])
response = fmgr.process_request(url, datagram, method)
return response
def fmgr_fwpol_package_folder(fmgr, paramgram):
"""
This function will create folders for firewall packages. It can create down to two levels deep.
We haven't yet tested for any more layers below two levels.
parent_folders for multiple levels may need to defined as "level1/level2/level3" for the URL parameters and such.
:param fmgr: The fmgr object instance from fmgr_utils.py
:type fmgr: class object
:param paramgram: The formatted dictionary of options to process
:type paramgram: dict
:return: The response from the FortiManager
:rtype: dict
"""
if paramgram["mode"] in ['set', 'add']:
url = '/pm/pkg/adom/{adom}'.format(adom=paramgram["adom"])
datagram = {
"type": paramgram["object_type"],
"name": paramgram["name"],
}
# IF PARENT FOLDER IS DEFINED
if paramgram["parent_folder"] is not None:
datagram = fmgr_fwpol_package_create_parent_folder_objects(paramgram, datagram)
# NORMAL DELETE NO PARENT
if paramgram["mode"] == "delete" and paramgram["parent_folder"] is None:
datagram = {
"name": paramgram["name"]
}
# SET DELETE URL
url = '/pm/pkg/adom/{adom}/{name}'.format(adom=paramgram["adom"], name=paramgram["name"])
# DELETE WITH PARENT
if paramgram["mode"] == "delete" and paramgram["parent_folder"] is not None:
datagram = {
"name": paramgram["name"]
}
# SET DELETE URL
url = '/pm/pkg/adom/{adom}/{parent_folder}/{name}'.format(adom=paramgram["adom"],
name=paramgram["name"],
parent_folder=paramgram["parent_folder"])
response = fmgr.process_request(url, datagram, paramgram["mode"])
return response
def fmgr_fwpol_package_install(fmgr, paramgram):
"""
This method/function installs FMGR FW Policy Packages to the scope members defined in the playbook.
:param fmgr: The fmgr object instance from fmgr_utils.py
:type fmgr: class object
:param paramgram: The formatted dictionary of options to process
:type paramgram: dict
:return: The response from the FortiManager
:rtype: dict
"""
datagram = {
"adom": paramgram["adom"],
"pkg": paramgram["name"],
}
if paramgram["parent_folder"]:
new_path = str(paramgram["parent_folder"]) + "/" + str(paramgram["name"])
datagram["pkg"] = new_path
# EXECUTE THE INSTALL REQUEST
url = '/securityconsole/install/package'
response = fmgr.process_request(url, datagram, FMGRMethods.EXEC)
return response
def fmgr_fwpol_package_get_details(fmgr, paramgram):
"""
This method/function will attempt to get existing package details, and append findings to the paramgram.
If nothing is found, the paramgram additions are simply empty.
:param fmgr: The fmgr object instance from fmgr_utils.py
:type fmgr: class object
:param paramgram: The formatted dictionary of options to process
:type paramgram: dict
:return: The response from the FortiManager
:rtype: dict
"""
# CHECK FOR SCOPE MEMBERS AND CREATE THAT MEMBERS LIST
# WE MUST PROPERLY FORMAT THE JSON FOR SCOPE MEMBERS WITH VDOMS
members_list = list()
if paramgram["scope_members"] is not None and paramgram["mode"] in ['add', 'set', 'add_targets', 'delete_targets']:
if isinstance(paramgram["scope_members"], list):
members = paramgram["scope_members"]
if isinstance(paramgram["scope_members"], str):
members = FMGRCommon.split_comma_strings_into_lists(paramgram["scope_members"])
for member in members:
scope_dict = {
"name": member,
"vdom": paramgram["scope_members_vdom"],
}
members_list.append(scope_dict)
# CHECK FOR SCOPE GROUPS AND ADD THAT TO THE MEMBERS LIST
# WE MUST PROPERLY FORMAT THE JSON FOR SCOPE GROUPS
if paramgram["scope_groups"] is not None and paramgram["mode"] in ['add', 'set', 'add_targets', 'delete_targets']:
if isinstance(paramgram["scope_groups"], list):
members = paramgram["scope_groups"]
if isinstance(paramgram["scope_groups"], str):
members = FMGRCommon.split_comma_strings_into_lists(paramgram["scope_groups"])
for member in members:
scope_dict = {
"name": member
}
members_list.append(scope_dict)
# CHECK FOR AN EXISTING POLICY PACKAGE, AND GET ITS MEMBERS SO WE DON'T OVERWRITE THEM WITH NOTHING
pol_datagram = {"type": paramgram["object_type"], "name": paramgram["name"]}
if paramgram["parent_folder"]:
pol_package_url = '/pm/pkg/adom/{adom}/{folder}/{pkg_name}'.format(adom=paramgram["adom"],
pkg_name=paramgram["name"],
folder=paramgram["parent_folder"])
else:
pol_package_url = '/pm/pkg/adom/{adom}/{pkg_name}'.format(adom=paramgram["adom"],
pkg_name=paramgram["name"])
pol_package = fmgr.process_request(pol_package_url, pol_datagram, FMGRMethods.GET)
existing_members = None
package_exists = None
if len(pol_package) == 2:
package_exists = True
try:
existing_members = pol_package[1]["scope member"]
except Exception as err:
existing_members = list()
else:
package_exists = False
# ADD COLLECTED DATA TO PARAMGRAM FOR USE IN METHODS
paramgram["existing_members_list"] = existing_members
paramgram["append_members_list"] = members_list
paramgram["package_exists"] = package_exists
return paramgram
def fmgr_fwpol_package_create_parent_folder_objects(paramgram, datagram):
"""
This function/method will take a paramgram with parent folders defined, and create the proper structure
so that objects are nested correctly.
:param paramgram: The paramgram used
:type paramgram: dict
:param datagram: The datagram, so far, as created by another function.
:type datagram: dict
:return: new_datagram
"""
# SPLIT THE PARENT FOLDER INTO A LIST BASED ON FORWARD SLASHES
# FORM THE DATAGRAM USING TEMPLATE ABOVE WITH THE PACKAGE NESTED IN A SUBOBJ
subobj_list = list()
subobj_list.append(datagram)
new_datagram = {
"type": "folder",
"name": paramgram["parent_folder"],
"subobj": subobj_list
}
parent_folders = paramgram["parent_folder"].split("/")
# LOOP THROUGH PARENT FOLDERS AND ADD AS MANY SUB OBJECT NESTED DICTS AS REQUIRED
# WE'RE BUILDING THE SUBOBJ NESTED OBJECT "INSIDE OUT"
num_of_parents = len(parent_folders)
if num_of_parents > 1:
parent_list_position = num_of_parents - 1
# REPLACE THE EXISTING PARENT FOLDER STRING WITH SLASHES, WITH THE BOTTOM MOST NESTED FOLDER
new_datagram["name"] = parent_folders[parent_list_position]
parent_list_position -= 1
while parent_list_position >= 0:
new_subobj_list = list()
new_subobj_list.append(new_datagram)
new_datagram = {
"type": "folder",
"name": parent_folders[parent_list_position],
"subobj": new_subobj_list
}
parent_list_position -= 1
# SET DATAGRAM TO THE NEWLY NESTED DATAGRAM
return new_datagram
def main():
argument_spec = dict(
adom=dict(required=False, type="str", default="root"),
mode=dict(choices=["add", "set", "delete", "move", "copy", "add_targets", "delete_targets", "install"],
type="str", default="add"),
name=dict(required=False, type="str"),
object_type=dict(required=True, type="str", choices=['pkg', 'folder']),
central_nat=dict(required=False, type="str", default="disable", choices=['enable', 'disable']),
fwpolicy_implicit_log=dict(required=False, type="str", default="disable", choices=['enable', 'disable']),
fwpolicy6_implicit_log=dict(required=False, type="str", default="disable", choices=['enable', 'disable']),
inspection_mode=dict(required=False, type="str", default="flow", choices=['flow', 'proxy']),
ngfw_mode=dict(required=False, type="str", default="profile-based", choices=['profile-based', 'policy-based']),
ssl_ssh_profile=dict(required=False, type="str"),
scope_groups=dict(required=False, type="str"),
scope_members=dict(required=False, type="str"),
scope_members_vdom=dict(required=False, type="str", default="root"),
parent_folder=dict(required=False, type="str"),
target_folder=dict(required=False, type="str"),
target_name=dict(required=False, type="str"),
)
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False,)
# MODULE DATAGRAM
paramgram = {
"adom": module.params["adom"],
"name": module.params["name"],
"mode": module.params["mode"],
"object_type": module.params["object_type"],
"central-nat": module.params["central_nat"],
"fwpolicy-implicit-log": module.params["fwpolicy_implicit_log"],
"fwpolicy6-implicit-log": module.params["fwpolicy6_implicit_log"],
"inspection-mode": module.params["inspection_mode"],
"ngfw-mode": module.params["ngfw_mode"],
"ssl-ssh-profile": module.params["ssl_ssh_profile"],
"scope_groups": module.params["scope_groups"],
"scope_members": module.params["scope_members"],
"scope_members_vdom": module.params["scope_members_vdom"],
"parent_folder": module.params["parent_folder"],
"target_folder": module.params["target_folder"],
"target_name": module.params["target_name"],
"append_members_list": list(),
"existing_members_list": list(),
"package_exists": None,
}
module.paramgram = paramgram
fmgr = None
if module._socket_path:
connection = Connection(module._socket_path)
fmgr = FortiManagerHandler(connection, module)
fmgr.tools = FMGRCommon()
else:
module.fail_json(**FAIL_SOCKET_MSG)
# BEGIN MODULE-SPECIFIC LOGIC -- THINGS NEED TO HAPPEN DEPENDING ON THE ENDPOINT AND OPERATION
results = DEFAULT_RESULT_OBJ
# QUERY FORTIMANAGER FOR EXISTING PACKAGE DETAILS AND UPDATE PARAMGRAM
paramgram = fmgr_fwpol_package_get_details(fmgr, paramgram)
try:
if paramgram["object_type"] == "pkg" and paramgram["mode"] in ["add", "set", "delete", "move", "copy"]:
results = fmgr_fwpol_package(fmgr, paramgram)
fmgr.govern_response(module=module, results=results,
ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram))
except Exception as err:
raise FMGBaseException(err)
try:
if paramgram["object_type"] == "pkg" and paramgram["package_exists"] \
and len(paramgram["append_members_list"]) > 0 \
and paramgram["mode"] in ['add_targets', 'delete_targets']:
results = fmgr_fwpol_package_edit_targets(fmgr, paramgram)
fmgr.govern_response(module=module, results=results,
ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram))
except Exception as err:
raise FMGBaseException(err)
try:
# IF THE object_type IS FOLDER LETS RUN THAT METHOD
if paramgram["object_type"] == "folder":
results = fmgr_fwpol_package_folder(fmgr, paramgram)
fmgr.govern_response(module=module, results=results,
ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram))
except Exception as err:
raise FMGBaseException(err)
try:
# IF THE object_type IS INSTALL AND NEEDED PARAMETERS ARE DEFINED INSTALL THE PACKAGE
if paramgram["name"] is not None and paramgram["object_type"] == "pkg" and paramgram["mode"] == "install":
results = fmgr_fwpol_package_install(fmgr, paramgram)
fmgr.govern_response(module=module, results=results,
ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram))
except Exception as err:
raise FMGBaseException(err)
return module.exit_json(**results[1])
if __name__ == "__main__":
main()