1#!/usr/bin/python 2from __future__ import (absolute_import, division, print_function) 3# Copyright 2019 Fortinet, Inc. 4# 5# This program 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# This program 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 this program. If not, see <https://www.gnu.org/licenses/>. 17 18__metaclass__ = type 19 20ANSIBLE_METADATA = {'status': ['preview'], 21 'supported_by': 'community', 22 'metadata_version': '1.1'} 23 24DOCUMENTATION = ''' 25--- 26module: fortios_firewall_addrgrp 27short_description: Configure IPv4 address groups in Fortinet's FortiOS and FortiGate. 28description: 29 - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the 30 user to set and modify firewall feature and addrgrp category. 31 Examples include all parameters and values need to be adjusted to datasources before usage. 32 Tested with FOS v6.0.5 33version_added: "2.8" 34author: 35 - Miguel Angel Munoz (@mamunozgonzalez) 36 - Nicolas Thomas (@thomnico) 37notes: 38 - Requires fortiosapi library developed by Fortinet 39 - Run as a local_action in your playbook 40requirements: 41 - fortiosapi>=0.9.8 42options: 43 host: 44 description: 45 - FortiOS or FortiGate IP address. 46 type: str 47 required: false 48 username: 49 description: 50 - FortiOS or FortiGate username. 51 type: str 52 required: false 53 password: 54 description: 55 - FortiOS or FortiGate password. 56 type: str 57 default: "" 58 vdom: 59 description: 60 - Virtual domain, among those defined previously. A vdom is a 61 virtual instance of the FortiGate that can be configured and 62 used as a different unit. 63 type: str 64 default: root 65 https: 66 description: 67 - Indicates if the requests towards FortiGate must use HTTPS protocol. 68 type: bool 69 default: true 70 ssl_verify: 71 description: 72 - Ensures FortiGate certificate must be verified by a proper CA. 73 type: bool 74 default: true 75 version_added: 2.9 76 state: 77 description: 78 - Indicates whether to create or remove the object. 79 This attribute was present already in previous version in a deeper level. 80 It has been moved out to this outer level. 81 type: str 82 required: false 83 choices: 84 - present 85 - absent 86 version_added: 2.9 87 firewall_addrgrp: 88 description: 89 - Configure IPv4 address groups. 90 default: null 91 type: dict 92 suboptions: 93 state: 94 description: 95 - B(Deprecated) 96 - Starting with Ansible 2.9 we recommend using the top-level 'state' parameter. 97 - HORIZONTALLINE 98 - Indicates whether to create or remove the object. 99 type: str 100 required: false 101 choices: 102 - present 103 - absent 104 allow_routing: 105 description: 106 - Enable/disable use of this group in the static route configuration. 107 type: str 108 choices: 109 - enable 110 - disable 111 color: 112 description: 113 - Color of icon on the GUI. 114 type: int 115 comment: 116 description: 117 - Comment. 118 type: str 119 member: 120 description: 121 - Address objects contained within the group. 122 type: list 123 suboptions: 124 name: 125 description: 126 - Address name. Source firewall.address.name firewall.addrgrp.name. 127 required: true 128 type: str 129 name: 130 description: 131 - Address group name. 132 required: true 133 type: str 134 tagging: 135 description: 136 - Config object tagging. 137 type: list 138 suboptions: 139 category: 140 description: 141 - Tag category. Source system.object-tagging.category. 142 type: str 143 name: 144 description: 145 - Tagging entry name. 146 required: true 147 type: str 148 tags: 149 description: 150 - Tags. 151 type: list 152 suboptions: 153 name: 154 description: 155 - Tag name. Source system.object-tagging.tags.name. 156 required: true 157 type: str 158 uuid: 159 description: 160 - Universally Unique Identifier (UUID; automatically assigned but can be manually reset). 161 type: str 162 visibility: 163 description: 164 - Enable/disable address visibility in the GUI. 165 type: str 166 choices: 167 - enable 168 - disable 169''' 170 171EXAMPLES = ''' 172- hosts: localhost 173 vars: 174 host: "192.168.122.40" 175 username: "admin" 176 password: "" 177 vdom: "root" 178 ssl_verify: "False" 179 tasks: 180 - name: Configure IPv4 address groups. 181 fortios_firewall_addrgrp: 182 host: "{{ host }}" 183 username: "{{ username }}" 184 password: "{{ password }}" 185 vdom: "{{ vdom }}" 186 https: "False" 187 state: "present" 188 firewall_addrgrp: 189 allow_routing: "enable" 190 color: "4" 191 comment: "Comment." 192 member: 193 - 194 name: "default_name_7 (source firewall.address.name firewall.addrgrp.name)" 195 name: "default_name_8" 196 tagging: 197 - 198 category: "<your_own_value> (source system.object-tagging.category)" 199 name: "default_name_11" 200 tags: 201 - 202 name: "default_name_13 (source system.object-tagging.tags.name)" 203 uuid: "<your_own_value>" 204 visibility: "enable" 205''' 206 207RETURN = ''' 208build: 209 description: Build number of the fortigate image 210 returned: always 211 type: str 212 sample: '1547' 213http_method: 214 description: Last method used to provision the content into FortiGate 215 returned: always 216 type: str 217 sample: 'PUT' 218http_status: 219 description: Last result given by FortiGate on last operation applied 220 returned: always 221 type: str 222 sample: "200" 223mkey: 224 description: Master key (id) used in the last call to FortiGate 225 returned: success 226 type: str 227 sample: "id" 228name: 229 description: Name of the table used to fulfill the request 230 returned: always 231 type: str 232 sample: "urlfilter" 233path: 234 description: Path of the table used to fulfill the request 235 returned: always 236 type: str 237 sample: "webfilter" 238revision: 239 description: Internal revision number 240 returned: always 241 type: str 242 sample: "17.0.2.10658" 243serial: 244 description: Serial number of the unit 245 returned: always 246 type: str 247 sample: "FGVMEVYYQT3AB5352" 248status: 249 description: Indication of the operation's result 250 returned: always 251 type: str 252 sample: "success" 253vdom: 254 description: Virtual domain used 255 returned: always 256 type: str 257 sample: "root" 258version: 259 description: Version of the FortiGate 260 returned: always 261 type: str 262 sample: "v5.6.3" 263 264''' 265 266from ansible.module_utils.basic import AnsibleModule 267from ansible.module_utils.connection import Connection 268from ansible.module_utils.network.fortios.fortios import FortiOSHandler 269from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG 270 271 272def login(data, fos): 273 host = data['host'] 274 username = data['username'] 275 password = data['password'] 276 ssl_verify = data['ssl_verify'] 277 278 fos.debug('on') 279 if 'https' in data and not data['https']: 280 fos.https('off') 281 else: 282 fos.https('on') 283 284 fos.login(host, username, password, verify=ssl_verify) 285 286 287def filter_firewall_addrgrp_data(json): 288 option_list = ['allow_routing', 'color', 'comment', 289 'member', 'name', 'tagging', 290 'uuid', 'visibility'] 291 dictionary = {} 292 293 for attribute in option_list: 294 if attribute in json and json[attribute] is not None: 295 dictionary[attribute] = json[attribute] 296 297 return dictionary 298 299 300def underscore_to_hyphen(data): 301 if isinstance(data, list): 302 for elem in data: 303 elem = underscore_to_hyphen(elem) 304 elif isinstance(data, dict): 305 new_data = {} 306 for k, v in data.items(): 307 new_data[k.replace('_', '-')] = underscore_to_hyphen(v) 308 data = new_data 309 310 return data 311 312 313def firewall_addrgrp(data, fos): 314 vdom = data['vdom'] 315 if 'state' in data and data['state']: 316 state = data['state'] 317 elif 'state' in data['firewall_addrgrp'] and data['firewall_addrgrp']: 318 state = data['firewall_addrgrp']['state'] 319 else: 320 state = True 321 firewall_addrgrp_data = data['firewall_addrgrp'] 322 filtered_data = underscore_to_hyphen(filter_firewall_addrgrp_data(firewall_addrgrp_data)) 323 324 if state == "present": 325 return fos.set('firewall', 326 'addrgrp', 327 data=filtered_data, 328 vdom=vdom) 329 330 elif state == "absent": 331 return fos.delete('firewall', 332 'addrgrp', 333 mkey=filtered_data['name'], 334 vdom=vdom) 335 336 337def is_successful_status(status): 338 return status['status'] == "success" or \ 339 status['http_method'] == "DELETE" and status['http_status'] == 404 340 341 342def fortios_firewall(data, fos): 343 344 if data['firewall_addrgrp']: 345 resp = firewall_addrgrp(data, fos) 346 347 return not is_successful_status(resp), \ 348 resp['status'] == "success", \ 349 resp 350 351 352def main(): 353 fields = { 354 "host": {"required": False, "type": "str"}, 355 "username": {"required": False, "type": "str"}, 356 "password": {"required": False, "type": "str", "default": "", "no_log": True}, 357 "vdom": {"required": False, "type": "str", "default": "root"}, 358 "https": {"required": False, "type": "bool", "default": True}, 359 "ssl_verify": {"required": False, "type": "bool", "default": True}, 360 "state": {"required": False, "type": "str", 361 "choices": ["present", "absent"]}, 362 "firewall_addrgrp": { 363 "required": False, "type": "dict", "default": None, 364 "options": { 365 "state": {"required": False, "type": "str", 366 "choices": ["present", "absent"]}, 367 "allow_routing": {"required": False, "type": "str", 368 "choices": ["enable", "disable"]}, 369 "color": {"required": False, "type": "int"}, 370 "comment": {"required": False, "type": "str"}, 371 "member": {"required": False, "type": "list", 372 "options": { 373 "name": {"required": True, "type": "str"} 374 }}, 375 "name": {"required": True, "type": "str"}, 376 "tagging": {"required": False, "type": "list", 377 "options": { 378 "category": {"required": False, "type": "str"}, 379 "name": {"required": True, "type": "str"}, 380 "tags": {"required": False, "type": "list", 381 "options": { 382 "name": {"required": True, "type": "str"} 383 }} 384 }}, 385 "uuid": {"required": False, "type": "str"}, 386 "visibility": {"required": False, "type": "str", 387 "choices": ["enable", "disable"]} 388 389 } 390 } 391 } 392 393 module = AnsibleModule(argument_spec=fields, 394 supports_check_mode=False) 395 396 # legacy_mode refers to using fortiosapi instead of HTTPAPI 397 legacy_mode = 'host' in module.params and module.params['host'] is not None and \ 398 'username' in module.params and module.params['username'] is not None and \ 399 'password' in module.params and module.params['password'] is not None 400 401 if not legacy_mode: 402 if module._socket_path: 403 connection = Connection(module._socket_path) 404 fos = FortiOSHandler(connection) 405 406 is_error, has_changed, result = fortios_firewall(module.params, fos) 407 else: 408 module.fail_json(**FAIL_SOCKET_MSG) 409 else: 410 try: 411 from fortiosapi import FortiOSAPI 412 except ImportError: 413 module.fail_json(msg="fortiosapi module is required") 414 415 fos = FortiOSAPI() 416 417 login(module.params, fos) 418 is_error, has_changed, result = fortios_firewall(module.params, fos) 419 fos.logout() 420 421 if not is_error: 422 module.exit_json(changed=has_changed, meta=result) 423 else: 424 module.fail_json(msg="Error in repo", meta=result) 425 426 427if __name__ == '__main__': 428 main() 429