1#!/usr/bin/python 2# 3# This file is part of Ansible 4# 5# Ansible is free software: you can redistribute it and/or modify 6# it under the terms of the GNU General Public License as published by 7# the Free Software Foundation, either version 3 of the License, or 8# (at your option) any later version. 9# 10# Ansible is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with Ansible. If not, see <http://www.gnu.org/licenses/>. 17# 18 19from __future__ import absolute_import, division, print_function 20__metaclass__ = type 21 22ANSIBLE_METADATA = { 23 "metadata_version": "1.1", 24 "status": ["preview"], 25 "supported_by": "community" 26} 27 28DOCUMENTATION = ''' 29--- 30module: fmgr_fwpol_package 31version_added: "2.8" 32notes: 33 - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). 34author: 35 - Luke Weighall (@lweighall) 36 - Andrew Welsh (@Ghilli3) 37 - Jim Huber (@p4r4n0y1ng) 38short_description: Manages FortiManager Firewall Policies Packages. 39description: 40 - Manages FortiManager Firewall Policies Packages. Policy Packages contain one or more Firewall Policies/Rules and 41 are distritbuted via FortiManager to Fortigates. 42 - This module controls the creation/edit/delete/assign of these packages. 43 44options: 45 adom: 46 description: 47 - The ADOM the configuration should belong to. 48 required: false 49 default: root 50 51 mode: 52 description: 53 - Sets one of three modes for managing the object. 54 choices: ['add', 'set', 'delete'] 55 default: add 56 57 name: 58 description: 59 - Name of the FortiManager package or folder. 60 required: True 61 62 object_type: 63 description: 64 - Are we managing packages or folders, or installing packages? 65 required: True 66 choices: ['pkg','folder','install'] 67 68 package_folder: 69 description: 70 - Name of the folder you want to put the package into. 71 required: false 72 73 central_nat: 74 description: 75 - Central NAT setting. 76 required: false 77 choices: ['enable', 'disable'] 78 default: disable 79 80 fwpolicy_implicit_log: 81 description: 82 - Implicit Log setting for all IPv4 policies in package. 83 required: false 84 choices: ['enable', 'disable'] 85 default: disable 86 87 fwpolicy6_implicit_log: 88 description: 89 - Implicit Log setting for all IPv6 policies in package. 90 required: false 91 choices: ['enable', 'disable'] 92 default: disable 93 94 inspection_mode: 95 description: 96 - Inspection mode setting for the policies flow or proxy. 97 required: false 98 choices: ['flow', 'proxy'] 99 default: flow 100 101 ngfw_mode: 102 description: 103 - NGFW mode setting for the policies flow or proxy. 104 required: false 105 choices: ['profile-based', 'policy-based'] 106 default: profile-based 107 108 ssl_ssh_profile: 109 description: 110 - if policy-based ngfw-mode, refer to firewall ssl-ssh-profile. 111 required: false 112 113 scope_members: 114 description: 115 - The devices or scope that you want to assign this policy package to. 116 required: false 117 118 scope_members_vdom: 119 description: 120 - The members VDOM you want to assign the package to. 121 required: false 122 default: root 123 124 parent_folder: 125 description: 126 - The parent folder name you want to add this object under. 127 required: false 128 129''' 130 131 132EXAMPLES = ''' 133- name: CREATE BASIC POLICY PACKAGE 134 fmgr_fwpol_package: 135 adom: "ansible" 136 mode: "add" 137 name: "testPackage" 138 object_type: "pkg" 139 140- name: ADD PACKAGE WITH TARGETS 141 fmgr_fwpol_package: 142 mode: "add" 143 adom: "ansible" 144 name: "ansibleTestPackage1" 145 object_type: "pkg" 146 inspection_mode: "flow" 147 ngfw_mode: "profile-based" 148 scope_members: "seattle-fgt02, seattle-fgt03" 149 150- name: ADD FOLDER 151 fmgr_fwpol_package: 152 mode: "add" 153 adom: "ansible" 154 name: "ansibleTestFolder1" 155 object_type: "folder" 156 157- name: ADD PACKAGE INTO PARENT FOLDER 158 fmgr_fwpol_package: 159 mode: "set" 160 adom: "ansible" 161 name: "ansibleTestPackage2" 162 object_type: "pkg" 163 parent_folder: "ansibleTestFolder1" 164 165- name: ADD FOLDER INTO PARENT FOLDER 166 fmgr_fwpol_package: 167 mode: "set" 168 adom: "ansible" 169 name: "ansibleTestFolder2" 170 object_type: "folder" 171 parent_folder: "ansibleTestFolder1" 172 173- name: INSTALL PACKAGE 174 fmgr_fwpol_package: 175 mode: "set" 176 adom: "ansible" 177 name: "ansibleTestPackage1" 178 object_type: "install" 179 scope_members: "seattle-fgt03, seattle-fgt02" 180 181- name: REMOVE PACKAGE 182 fmgr_fwpol_package: 183 mode: "delete" 184 adom: "ansible" 185 name: "ansibleTestPackage1" 186 object_type: "pkg" 187 188- name: REMOVE NESTED PACKAGE 189 fmgr_fwpol_package: 190 mode: "delete" 191 adom: "ansible" 192 name: "ansibleTestPackage2" 193 object_type: "pkg" 194 parent_folder: "ansibleTestFolder1" 195 196- name: REMOVE NESTED FOLDER 197 fmgr_fwpol_package: 198 mode: "delete" 199 adom: "ansible" 200 name: "ansibleTestFolder2" 201 object_type: "folder" 202 parent_folder: "ansibleTestFolder1" 203 204- name: REMOVE FOLDER 205 fmgr_fwpol_package: 206 mode: "delete" 207 adom: "ansible" 208 name: "ansibleTestFolder1" 209 object_type: "folder" 210''' 211RETURN = """ 212api_result: 213 description: full API response, includes status code and message 214 returned: always 215 type: str 216""" 217 218from ansible.module_utils.basic import AnsibleModule 219from ansible.module_utils.connection import Connection 220from ansible.module_utils.network.fortimanager.fortimanager import FortiManagerHandler 221from ansible.module_utils.network.fortimanager.common import FMGBaseException 222from ansible.module_utils.network.fortimanager.common import FMGRCommon 223from ansible.module_utils.network.fortimanager.common import DEFAULT_RESULT_OBJ 224from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG 225from ansible.module_utils.network.fortimanager.common import FMGRMethods 226 227 228def fmgr_fwpol_package(fmgr, paramgram): 229 """ 230 This function will create FMGR Firewall Policy Packages, or delete them. It is also capable of assigning packages. 231 This function DOES NOT install the package. See the function fmgr_fwpol_package_install() 232 233 :param fmgr: The fmgr object instance from fmgr_utils.py 234 :type fmgr: class object 235 :param paramgram: The formatted dictionary of options to process 236 :type paramgram: dict 237 238 :return: The response from the FortiManager 239 :rtype: dict 240 """ 241 if paramgram["mode"] in ['set', 'add']: 242 url = '/pm/pkg/adom/{adom}'.format(adom=paramgram["adom"]) 243 members_list = [] 244 245 # CHECK FOR SCOPE MEMBERS AND CREATE THAT DICT 246 if paramgram["scope_members"] is not None: 247 members = FMGRCommon.split_comma_strings_into_lists(paramgram["scope_members"]) 248 for member in members: 249 scope_dict = { 250 "name": member, 251 "vdom": paramgram["scope_members_vdom"], 252 } 253 members_list.append(scope_dict) 254 255 # IF PARENT FOLDER IS NOT DEFINED 256 if paramgram["parent_folder"] is None: 257 datagram = { 258 "type": paramgram["object_type"], 259 "name": paramgram["name"], 260 "scope member": members_list, 261 "package settings": { 262 "central-nat": paramgram["central-nat"], 263 "fwpolicy-implicit-log": paramgram["fwpolicy-implicit-log"], 264 "fwpolicy6-implicit-log": paramgram["fwpolicy6-implicit-log"], 265 "inspection-mode": paramgram["inspection-mode"], 266 "ngfw-mode": paramgram["ngfw-mode"], 267 } 268 } 269 270 if paramgram["ngfw-mode"] == "policy-based" and paramgram["ssl-ssh-profile"] is not None: 271 datagram["package settings"]["ssl-ssh-profile"] = paramgram["ssl-ssh-profile"] 272 273 # IF PARENT FOLDER IS DEFINED 274 if paramgram["parent_folder"] is not None: 275 datagram = { 276 "type": "folder", 277 "name": paramgram["parent_folder"], 278 "subobj": [{ 279 "name": paramgram["name"], 280 "scope member": members_list, 281 "type": "pkg", 282 "package settings": { 283 "central-nat": paramgram["central-nat"], 284 "fwpolicy-implicit-log": paramgram["fwpolicy-implicit-log"], 285 "fwpolicy6-implicit-log": paramgram["fwpolicy6-implicit-log"], 286 "inspection-mode": paramgram["inspection-mode"], 287 "ngfw-mode": paramgram["ngfw-mode"], 288 } 289 }] 290 } 291 292 # NORMAL DELETE NO PARENT 293 if paramgram["mode"] == "delete" and paramgram["parent_folder"] is None: 294 datagram = { 295 "name": paramgram["name"] 296 } 297 # SET DELETE URL 298 url = '/pm/pkg/adom/{adom}/{name}'.format(adom=paramgram["adom"], name=paramgram["name"]) 299 300 # DELETE WITH PARENT 301 if paramgram["mode"] == "delete" and paramgram["parent_folder"] is not None: 302 datagram = { 303 "name": paramgram["name"] 304 } 305 # SET DELETE URL 306 url = '/pm/pkg/adom/{adom}/{parent_folder}/{name}'.format(adom=paramgram["adom"], 307 name=paramgram["name"], 308 parent_folder=paramgram["parent_folder"]) 309 310 response = fmgr.process_request(url, datagram, paramgram["mode"]) 311 return response 312 313 314def fmgr_fwpol_package_folder(fmgr, paramgram): 315 """ 316 This function will create folders for firewall packages. It can create down to two levels deep. 317 We haven't yet tested for any more layers below two levels. 318 parent_folders for multiple levels may need to defined as "level1/level2/level3" for the URL parameters and such. 319 320 :param fmgr: The fmgr object instance from fmgr_utils.py 321 :type fmgr: class object 322 :param paramgram: The formatted dictionary of options to process 323 :type paramgram: dict 324 325 :return: The response from the FortiManager 326 :rtype: dict 327 """ 328 if paramgram["mode"] in ['set', 'add']: 329 url = '/pm/pkg/adom/{adom}'.format(adom=paramgram["adom"]) 330 # IF PARENT FOLDER IS NOT DEFINED 331 if paramgram["parent_folder"] is None: 332 datagram = { 333 "type": paramgram["object_type"], 334 "name": paramgram["name"], 335 } 336 337 # IF PARENT FOLDER IS DEFINED 338 if paramgram["parent_folder"] is not None: 339 datagram = { 340 "type": paramgram["object_type"], 341 "name": paramgram["parent_folder"], 342 "subobj": [{ 343 "name": paramgram["name"], 344 "type": paramgram["object_type"], 345 346 }] 347 } 348 # NORMAL DELETE NO PARENT 349 if paramgram["mode"] == "delete" and paramgram["parent_folder"] is None: 350 datagram = { 351 "name": paramgram["name"] 352 } 353 # SET DELETE URL 354 url = '/pm/pkg/adom/{adom}/{name}'.format(adom=paramgram["adom"], name=paramgram["name"]) 355 356 # DELETE WITH PARENT 357 if paramgram["mode"] == "delete" and paramgram["parent_folder"] is not None: 358 datagram = { 359 "name": paramgram["name"] 360 } 361 # SET DELETE URL 362 url = '/pm/pkg/adom/{adom}/{parent_folder}/{name}'.format(adom=paramgram["adom"], 363 name=paramgram["name"], 364 parent_folder=paramgram["parent_folder"]) 365 366 response = fmgr.process_request(url, datagram, paramgram["mode"]) 367 return response 368 369 370def fmgr_fwpol_package_install(fmgr, paramgram): 371 """ 372 This method/function installs FMGR FW Policy Packages to the scope members defined in the playbook. 373 374 :param fmgr: The fmgr object instance from fmgr_utils.py 375 :type fmgr: class object 376 :param paramgram: The formatted dictionary of options to process 377 :type paramgram: dict 378 379 :return: The response from the FortiManager 380 :rtype: dict 381 """ 382 # INIT BLANK MEMBERS LIST 383 members_list = [] 384 # USE THE PARSE CSV FUNCTION TO GET A LIST FORMAT OF THE MEMBERS 385 members = FMGRCommon.split_comma_strings_into_lists(paramgram["scope_members"]) 386 # USE THAT LIST TO BUILD THE DICTIONARIES NEEDED, AND ADD TO THE BLANK MEMBERS LIST 387 for member in members: 388 scope_dict = { 389 "name": member, 390 "vdom": paramgram["scope_members_vdom"], 391 } 392 members_list.append(scope_dict) 393 # THEN FOR THE DATAGRAM, USING THE MEMBERS LIST CREATED ABOVE 394 datagram = { 395 "adom": paramgram["adom"], 396 "pkg": paramgram["name"], 397 "scope": members_list 398 } 399 # EXECUTE THE INSTALL REQUEST 400 url = '/securityconsole/install/package' 401 response = fmgr.process_request(url, datagram, FMGRMethods.EXEC) 402 return response 403 404 405def main(): 406 argument_spec = dict( 407 adom=dict(required=False, type="str", default="root"), 408 mode=dict(choices=["add", "set", "delete"], type="str", default="add"), 409 410 name=dict(required=False, type="str"), 411 object_type=dict(required=True, type="str", choices=['pkg', 'folder', 'install']), 412 package_folder=dict(required=False, type="str"), 413 central_nat=dict(required=False, type="str", default="disable", choices=['enable', 'disable']), 414 fwpolicy_implicit_log=dict(required=False, type="str", default="disable", choices=['enable', 'disable']), 415 fwpolicy6_implicit_log=dict(required=False, type="str", default="disable", choices=['enable', 'disable']), 416 inspection_mode=dict(required=False, type="str", default="flow", choices=['flow', 'proxy']), 417 ngfw_mode=dict(required=False, type="str", default="profile-based", choices=['profile-based', 'policy-based']), 418 ssl_ssh_profile=dict(required=False, type="str"), 419 scope_members=dict(required=False, type="str"), 420 scope_members_vdom=dict(required=False, type="str", default="root"), 421 parent_folder=dict(required=False, type="str"), 422 423 ) 424 425 module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) 426 # MODULE DATAGRAM 427 paramgram = { 428 "adom": module.params["adom"], 429 "name": module.params["name"], 430 "mode": module.params["mode"], 431 "object_type": module.params["object_type"], 432 "package-folder": module.params["package_folder"], 433 "central-nat": module.params["central_nat"], 434 "fwpolicy-implicit-log": module.params["fwpolicy_implicit_log"], 435 "fwpolicy6-implicit-log": module.params["fwpolicy6_implicit_log"], 436 "inspection-mode": module.params["inspection_mode"], 437 "ngfw-mode": module.params["ngfw_mode"], 438 "ssl-ssh-profile": module.params["ssl_ssh_profile"], 439 "scope_members": module.params["scope_members"], 440 "scope_members_vdom": module.params["scope_members_vdom"], 441 "parent_folder": module.params["parent_folder"], 442 } 443 module.paramgram = paramgram 444 fmgr = None 445 if module._socket_path: 446 connection = Connection(module._socket_path) 447 fmgr = FortiManagerHandler(connection, module) 448 fmgr.tools = FMGRCommon() 449 else: 450 module.fail_json(**FAIL_SOCKET_MSG) 451 452 # BEGIN MODULE-SPECIFIC LOGIC -- THINGS NEED TO HAPPEN DEPENDING ON THE ENDPOINT AND OPERATION 453 results = DEFAULT_RESULT_OBJ 454 455 try: 456 if paramgram["object_type"] == "pkg": 457 results = fmgr_fwpol_package(fmgr, paramgram) 458 fmgr.govern_response(module=module, results=results, 459 ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) 460 except Exception as err: 461 raise FMGBaseException(err) 462 463 try: 464 # IF THE object_type IS FOLDER LETS RUN THAT METHOD 465 if paramgram["object_type"] == "folder": 466 results = fmgr_fwpol_package_folder(fmgr, paramgram) 467 fmgr.govern_response(module=module, results=results, 468 ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) 469 except Exception as err: 470 raise FMGBaseException(err) 471 472 try: 473 # IF THE object_type IS INSTALL AND NEEDED PARAMETERS ARE DEFINED INSTALL THE PACKAGE 474 if paramgram["scope_members"] is not None and paramgram["name"] is not None and\ 475 paramgram["object_type"] == "install": 476 results = fmgr_fwpol_package_install(fmgr, paramgram) 477 fmgr.govern_response(module=module, results=results, 478 ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) 479 except Exception as err: 480 raise FMGBaseException(err) 481 482 return module.exit_json(**results[1]) 483 484 485if __name__ == "__main__": 486 main() 487