fmgr_fwobj_address¶
Metadata¶
Name: fmgr_fwobj_address
Description: Allows for the management of IPv4, IPv6, and multicast address objects within FortiManager.
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
allow_routing¶
- Description: Enable/disable use of this address in the static route configuration.
- default: disable
- choices: [‘enable’, ‘disable’]
associated_interface¶
- Description: Associated interface name.
cache_ttl¶
- Description: Minimal TTL of individual IP addresses in FQDN cache. Only applies when type = wildcard-fqdn.
color¶
Description: Color of the object in FortiManager GUI.
Takes integers 1-32
default: 22
comment¶
- Description: Comment for the object in FortiManager.
country¶
- Description: Country name. Required if type = geographic.
end_ip¶
- Description: End IP. Only used when ipv4 = iprange.
fqdn¶
- Description: Fully qualified domain name.
group_members¶
- Description: Address group member. If this is defined w/out group_name, the operation will fail.
group_name¶
- Description: Address group name. If this is defined in playbook task, all other options are ignored.
ipv4¶
Description: Type of IPv4 Object.
Must not be specified with either multicast or IPv6 parameters.
choices: [‘ipmask’, ‘iprange’, ‘fqdn’, ‘wildcard’, ‘geography’, ‘wildcard-fqdn’, ‘group’]
ipv4addr¶
Description: IP and network mask. If only defining one IP use this parameter. (i.e. 10.7.220.30/255.255.255.255)
Can also define subnets (i.e. 10.7.220.0/255.255.255.0)
Also accepts CIDR (i.e. 10.7.220.0/24)
If Netmask is omitted after IP address, /32 is assumed.
When multicast is set to Broadcast Subnet the ipv4addr parameter is used to specify the subnet.
ipv6¶
Description: Puts module into IPv6 mode.
Must not be specified with either ipv4 or multicast parameters.
choices: [‘ip’, ‘iprange’, ‘group’]
ipv6addr¶
- Description: IPv6 address in full. (i.e. 2001:0db8:85a3:0000:0000:8a2e:0370:7334)
mode¶
- Description: Sets one of three modes for managing the object.
- default: add
- choices: [‘add’, ‘set’, ‘delete’]
multicast¶
Description: Manages Multicast Address Objects.
Sets either a Multicast IP Range or a Broadcast Subnet.
Must not be specified with either ipv4 or ipv6 parameters.
When set to Broadcast Subnet the ipv4addr parameter is used to specify the subnet.
Can create IPv4 Multicast Objects (multicastrange and broadcastmask options – uses start/end-ip and ipv4addr).
choices: [‘multicastrange’, ‘broadcastmask’, ‘ip6’]
name¶
- Description: Friendly Name Address object name in FortiManager.
obj_id¶
- Description: Object ID for NSX.
start_ip¶
- Description: Start IP. Only used when ipv4 = iprange.
visibility¶
- Description: Enable/disable address visibility.
- default: enable
- choices: [‘enable’, ‘disable’]
wildcard¶
- Description: IP address and wildcard netmask. Required if ipv4 = wildcard.
wildcard_fqdn¶
- Description: Wildcard FQDN. Required if ipv4 = wildcard-fqdn.
Functions¶
- fmgr_fwobj_ipv4
def fmgr_fwobj_ipv4(fmgr, paramgram): """ :param fmgr: The fmgr object instance from fortimanager.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 """ # EVAL THE MODE PARAMETER FOR SET OR ADD if paramgram["mode"] in ['set', 'add']: # CREATE THE DATAGRAM DICTIONARY # ENSURE THE DATAGRAM KEYS MATCH THE JSON API GUIDE ATTRIBUTES, NOT WHAT IS IN ANSIBLE # SOME PARAMETERS SHOWN IN THIS DICTIONARY WE DON'T EVEN ASK THE USER FOR IN PLAYBOOKS BUT ARE REQUIRED datagram = { "comment": paramgram["comment"], "associated-interface": paramgram["associated-interface"], "cache-ttl": paramgram["cache-ttl"], "name": paramgram["name"], "allow-routing": paramgram["allow-routing"], "color": paramgram["color"], "meta fields": {}, "dynamic_mapping": [], "visibility": paramgram["allow-routing"], "type": paramgram["ipv4"], } # SET THE CORRECT URL BASED ON THE TYPE (WE'RE DOING GROUPS IN THIS METHOD, TOO) if datagram["type"] == "group": url = '/pm/config/adom/{adom}/obj/firewall/addrgrp'.format(adom=paramgram["adom"]) else: url = '/pm/config/adom/{adom}/obj/firewall/address'.format(adom=paramgram["adom"]) ######################### # IF type = 'ipmask' ######################### if datagram["type"] == "ipmask": # CREATE THE SUBNET LIST OBJECT subnet = [] # EVAL THE IPV4ADDR INPUT AND SPLIT THE IP ADDRESS FROM THE MASK AND APPEND THEM TO THE SUBNET LIST for subnets in paramgram["ipv4addr"].split("/"): subnet.append(subnets) # CHECK THAT THE SECOND ENTRY IN THE SUBNET LIST (WHAT WAS TO THE RIGHT OF THE / CHARACTER) # IS IN SUBNET MASK FORMAT AND NOT CIDR FORMAT. # IF IT IS IN CIDR FORMAT, WE NEED TO CONVERT IT TO SUBNET BIT MASK FORMAT FOR THE JSON API if not re.match(r'\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}', subnet[1]): # IF THE SUBNET PARAMETER INPUT DIDN'T LOOK LIKE xxx.xxx.xxx.xxx TO REGEX... # ... RUN IT THROUGH THE CIDR_TO_NETMASK() FUNCTION mask = fmgr._tools.cidr_to_netmask(subnet[1]) # AND THEN UPDATE THE SUBNET LIST OBJECT subnet[1] = mask # INCLUDE THE SUBNET LIST OBJECT IN THE DATAGRAM DICTIONARY TO BE SUBMITTED datagram["subnet"] = subnet ######################### # IF type = 'iprange' ######################### if datagram["type"] == "iprange": datagram["start-ip"] = paramgram["start-ip"] datagram["end-ip"] = paramgram["end-ip"] datagram["subnet"] = ["0.0.0.0", "0.0.0.0"] ######################### # IF type = 'geography' ######################### if datagram["type"] == "geography": datagram["country"] = paramgram["country"] ######################### # IF type = 'wildcard' ######################### if datagram["type"] == "wildcard": subnet = [] for subnets in paramgram["wildcard"].split("/"): subnet.append(subnets) if not re.match(r'\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}', subnet[1]): mask = fmgr._tools.cidr_to_netmask(subnet[1]) subnet[1] = mask datagram["wildcard"] = subnet ######################### # IF type = 'wildcard-fqdn' ######################### if datagram["type"] == "wildcard-fqdn": datagram["wildcard-fqdn"] = paramgram["wildcard-fqdn"] ######################### # IF type = 'fqdn' ######################### if datagram["type"] == "fqdn": datagram["fqdn"] = paramgram["fqdn"] ######################### # IF type = 'group' ######################### if datagram["type"] == "group": datagram = { "comment": paramgram["comment"], "name": paramgram["group_name"], "color": paramgram["color"], "meta fields": {}, "dynamic_mapping": [], "visibility": paramgram["visibility"] } members = [] group_members = paramgram["group_members"].replace(" ", "") try: for member in group_members.split(","): members.append(member) except Exception: pass datagram["member"] = members # EVAL THE MODE PARAMETER FOR DELETE if paramgram["mode"] == "delete": # IF A GROUP, SET THE CORRECT NAME AND URL FOR THE GROUP ENDPOINT if paramgram["ipv4"] == "group": datagram = {} url = '/pm/config/adom/{adom}/obj/firewall/addrgrp/{name}'.format(adom=paramgram["adom"], name=paramgram["group_name"]) # OTHERWISE WE'RE JUST GOING TO USE THE ADDRESS ENDPOINT else: datagram = {} url = '/pm/config/adom/{adom}/obj/firewall/address/{name}'.format(adom=paramgram["adom"], name=paramgram["name"]) response = fmgr.process_request(url, datagram, paramgram["mode"]) return response
- fmgr_fwobj_ipv6
def fmgr_fwobj_ipv6(fmgr, paramgram): """ :param fmgr: The fmgr object instance from fortimanager.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 """ # EVAL THE MODE PARAMETER FOR SET OR ADD if paramgram["mode"] in ['set', 'add']: # CREATE THE DATAGRAM DICTIONARY # ENSURE THE DATAGRAM KEYS MATCH THE JSON API GUIDE ATTRIBUTES, NOT WHAT IS IN ANSIBLE # SOME PARAMETERS SHOWN IN THIS DICTIONARY WE DON'T EVEN ASK THE USER FOR IN PLAYBOOKS BUT ARE REQUIRED datagram = { "comment": paramgram["comment"], "name": paramgram["name"], "color": paramgram["color"], "dynamic_mapping": [], "visibility": paramgram["visibility"], "type": paramgram["ipv6"] } # SET THE CORRECT URL BASED ON THE TYPE (WE'RE DOING GROUPS IN THIS METHOD, TOO) if datagram["type"] == "group": url = '/pm/config/adom/{adom}/obj/firewall/addrgrp6'.format(adom=paramgram["adom"]) else: url = '/pm/config/adom/{adom}/obj/firewall/address6'.format(adom=paramgram["adom"]) ######################### # IF type = 'ip' ######################### if datagram["type"] == "ip": datagram["type"] = "ipprefix" datagram["ip6"] = paramgram["ipv6addr"] ######################### # IF type = 'iprange' ######################### if datagram["type"] == "iprange": datagram["start-ip"] = paramgram["start-ip"] datagram["end-ip"] = paramgram["end-ip"] ######################### # IF type = 'group' ######################### if datagram["type"] == "group": datagram = None datagram = { "comment": paramgram["comment"], "name": paramgram["group_name"], "color": paramgram["color"], "visibility": paramgram["visibility"] } members = [] group_members = paramgram["group_members"].replace(" ", "") try: for member in group_members.split(","): members.append(member) except Exception: pass datagram["member"] = members # EVAL THE MODE PARAMETER FOR DELETE if paramgram["mode"] == "delete": # IF A GROUP, SET THE CORRECT NAME AND URL FOR THE GROUP ENDPOINT if paramgram["ipv6"] == "group": datagram = {} url = '/pm/config/adom/{adom}/obj/firewall/addrgrp6/{name}'.format(adom=paramgram["adom"], name=paramgram["group_name"]) # OTHERWISE WE'RE JUST GOING TO USE THE ADDRESS ENDPOINT else: datagram = {} url = '/pm/config/adom/{adom}/obj/firewall/address6/{name}'.format(adom=paramgram["adom"], name=paramgram["name"]) response = fmgr.process_request(url, datagram, paramgram["mode"]) return response
- fmgr_fwobj_multicast
def fmgr_fwobj_multicast(fmgr, paramgram): """ :param fmgr: The fmgr object instance from fortimanager.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 """ # EVAL THE MODE PARAMETER FOR SET OR ADD if paramgram["mode"] in ['set', 'add']: # CREATE THE DATAGRAM DICTIONARY # ENSURE THE DATAGRAM KEYS MATCH THE JSON API GUIDE ATTRIBUTES, NOT WHAT IS IN ANSIBLE # SOME PARAMETERS SHOWN IN THIS DICTIONARY WE DON'T EVEN ASK THE USER FOR IN PLAYBOOKS BUT ARE REQUIRED datagram = { "associated-interface": paramgram["associated-interface"], "comment": paramgram["comment"], "name": paramgram["name"], "color": paramgram["color"], "type": paramgram["multicast"], "visibility": paramgram["visibility"], } # SET THE CORRECT URL url = '/pm/config/adom/{adom}/obj/firewall/multicast-address'.format(adom=paramgram["adom"]) ######################### # IF type = 'multicastrange' ######################### if paramgram["multicast"] == "multicastrange": datagram["start-ip"] = paramgram["start-ip"] datagram["end-ip"] = paramgram["end-ip"] datagram["subnet"] = ["0.0.0.0", "0.0.0.0"] ######################### # IF type = 'broadcastmask' ######################### if paramgram["multicast"] == "broadcastmask": # EVAL THE IPV4ADDR INPUT AND SPLIT THE IP ADDRESS FROM THE MASK AND APPEND THEM TO THE SUBNET LIST subnet = [] for subnets in paramgram["ipv4addr"].split("/"): subnet.append(subnets) # CHECK THAT THE SECOND ENTRY IN THE SUBNET LIST (WHAT WAS TO THE RIGHT OF THE / CHARACTER) # IS IN SUBNET MASK FORMAT AND NOT CIDR FORMAT. # IF IT IS IN CIDR FORMAT, WE NEED TO CONVERT IT TO SUBNET BIT MASK FORMAT FOR THE JSON API if not re.match(r'\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}', subnet[1]): # IF THE SUBNET PARAMETER INPUT DIDN'T LOOK LIKE 255.255.255.255 TO REGEX... # ... RUN IT THROUGH THE fmgr_cidr_to_netmask() FUNCTION mask = fmgr._tools.cidr_to_netmask(subnet[1]) # AND THEN UPDATE THE SUBNET LIST OBJECT subnet[1] = mask # INCLUDE THE SUBNET LIST OBJECT IN THE DATAGRAM DICTIONARY TO BE SUBMITTED datagram["subnet"] = subnet # EVAL THE MODE PARAMETER FOR DELETE if paramgram["mode"] == "delete": datagram = { "name": paramgram["name"] } # SET THE CORRECT URL FOR DELETE url = '/pm/config/adom/{adom}/obj/firewall/multicast-address/{name}'.format(adom=paramgram["adom"], name=paramgram["name"]) response = fmgr.process_request(url, datagram, paramgram["mode"]) return response
- main
def main(): argument_spec = dict( adom=dict(required=False, type="str", default="root"), mode=dict(choices=["add", "set", "delete"], type="str", default="add"), allow_routing=dict(required=False, type="str", choices=['enable', 'disable'], default="disable"), associated_interface=dict(required=False, type="str"), cache_ttl=dict(required=False, type="str"), color=dict(required=False, type="str", default=22), comment=dict(required=False, type="str"), country=dict(required=False, type="str"), fqdn=dict(required=False, type="str"), name=dict(required=False, type="str"), start_ip=dict(required=False, type="str"), end_ip=dict(required=False, type="str"), ipv4=dict(required=False, type="str", choices=['ipmask', 'iprange', 'fqdn', 'wildcard', 'geography', 'wildcard-fqdn', 'group']), visibility=dict(required=False, type="str", choices=['enable', 'disable'], default="enable"), wildcard=dict(required=False, type="str"), wildcard_fqdn=dict(required=False, type="str"), ipv6=dict(required=False, type="str", choices=['ip', 'iprange', 'group']), group_members=dict(required=False, type="str"), group_name=dict(required=False, type="str"), ipv4addr=dict(required=False, type="str"), ipv6addr=dict(required=False, type="str"), multicast=dict(required=False, type="str", choices=['multicastrange', 'broadcastmask', 'ip6']), obj_id=dict(required=False, type="str"), ) module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, mutually_exclusive=[ ['ipv4', 'ipv6'], ['ipv4', 'multicast'], ['ipv6', 'multicast'] ]) paramgram = { "adom": module.params["adom"], "allow-routing": module.params["allow_routing"], "associated-interface": module.params["associated_interface"], "cache-ttl": module.params["cache_ttl"], "color": module.params["color"], "comment": module.params["comment"], "country": module.params["country"], "end-ip": module.params["end_ip"], "fqdn": module.params["fqdn"], "name": module.params["name"], "start-ip": module.params["start_ip"], "visibility": module.params["visibility"], "wildcard": module.params["wildcard"], "wildcard-fqdn": module.params["wildcard_fqdn"], "ipv6": module.params["ipv6"], "ipv4": module.params["ipv4"], "group_members": module.params["group_members"], "group_name": module.params["group_name"], "ipv4addr": module.params["ipv4addr"], "ipv6addr": module.params["ipv6addr"], "multicast": module.params["multicast"], "mode": module.params["mode"], "obj-id": module.params["obj_id"], } 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) results = DEFAULT_RESULT_OBJ try: if paramgram["ipv4"]: results = fmgr_fwobj_ipv4(fmgr, paramgram) elif paramgram["ipv6"]: results = fmgr_fwobj_ipv6(fmgr, paramgram) elif paramgram["multicast"]: results = fmgr_fwobj_multicast(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) if results is not None: return module.exit_json(**results[1]) else: return module.exit_json(msg="Couldn't find a proper ipv4 or ipv6 or multicast parameter " "to run in the logic tree. Exiting...")
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_fwobj_address
version_added: "2.8"
notes:
- Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/).
author:
- Luke Weighall (@lweighall)
- Andrew Welsh (@Ghilli3)
- Jim Huber (@p4r4n0y1ng)
short_description: Allows the management of firewall objects in FortiManager
description:
- Allows for the management of IPv4, IPv6, and multicast address objects within FortiManager.
options:
adom:
description:
- The ADOM the configuration should belong to.
required: false
default: root
allow_routing:
description:
- Enable/disable use of this address in the static route configuration.
choices: ['enable', 'disable']
default: 'disable'
associated_interface:
description:
- Associated interface name.
cache_ttl:
description:
- Minimal TTL of individual IP addresses in FQDN cache. Only applies when type = wildcard-fqdn.
color:
description:
- Color of the object in FortiManager GUI.
- Takes integers 1-32
default: 22
comment:
description:
- Comment for the object in FortiManager.
country:
description:
- Country name. Required if type = geographic.
end_ip:
description:
- End IP. Only used when ipv4 = iprange.
group_members:
description:
- Address group member. If this is defined w/out group_name, the operation will fail.
group_name:
description:
- Address group name. If this is defined in playbook task, all other options are ignored.
ipv4:
description:
- Type of IPv4 Object.
- Must not be specified with either multicast or IPv6 parameters.
choices: ['ipmask', 'iprange', 'fqdn', 'wildcard', 'geography', 'wildcard-fqdn', 'group']
ipv4addr:
description:
- IP and network mask. If only defining one IP use this parameter. (i.e. 10.7.220.30/255.255.255.255)
- Can also define subnets (i.e. 10.7.220.0/255.255.255.0)
- Also accepts CIDR (i.e. 10.7.220.0/24)
- If Netmask is omitted after IP address, /32 is assumed.
- When multicast is set to Broadcast Subnet the ipv4addr parameter is used to specify the subnet.
ipv6:
description:
- Puts module into IPv6 mode.
- Must not be specified with either ipv4 or multicast parameters.
choices: ['ip', 'iprange', 'group']
ipv6addr:
description:
- IPv6 address in full. (i.e. 2001:0db8:85a3:0000:0000:8a2e:0370:7334)
fqdn:
description:
- Fully qualified domain name.
mode:
description:
- Sets one of three modes for managing the object.
choices: ['add', 'set', 'delete']
default: add
multicast:
description:
- Manages Multicast Address Objects.
- Sets either a Multicast IP Range or a Broadcast Subnet.
- Must not be specified with either ipv4 or ipv6 parameters.
- When set to Broadcast Subnet the ipv4addr parameter is used to specify the subnet.
- Can create IPv4 Multicast Objects (multicastrange and broadcastmask options -- uses start/end-ip and ipv4addr).
choices: ['multicastrange', 'broadcastmask', 'ip6']
name:
description:
- Friendly Name Address object name in FortiManager.
obj_id:
description:
- Object ID for NSX.
start_ip:
description:
- Start IP. Only used when ipv4 = iprange.
visibility:
description:
- Enable/disable address visibility.
choices: ['enable', 'disable']
default: 'enable'
wildcard:
description:
- IP address and wildcard netmask. Required if ipv4 = wildcard.
wildcard_fqdn:
description:
- Wildcard FQDN. Required if ipv4 = wildcard-fqdn.
'''
EXAMPLES = '''
- name: ADD IPv4 IP ADDRESS OBJECT
fmgr_fwobj_address:
ipv4: "ipmask"
ipv4addr: "10.7.220.30/32"
name: "ansible_v4Obj"
comment: "Created by Ansible"
color: "6"
- name: ADD IPv4 IP ADDRESS OBJECT MORE OPTIONS
fmgr_fwobj_address:
ipv4: "ipmask"
ipv4addr: "10.7.220.34/32"
name: "ansible_v4Obj_MORE"
comment: "Created by Ansible"
color: "6"
allow_routing: "enable"
cache_ttl: "180"
associated_interface: "port1"
obj_id: "123"
- name: ADD IPv4 IP ADDRESS SUBNET OBJECT
fmgr_fwobj_address:
ipv4: "ipmask"
ipv4addr: "10.7.220.0/255.255.255.128"
name: "ansible_subnet"
comment: "Created by Ansible"
mode: "set"
- name: ADD IPv4 IP ADDRESS RANGE OBJECT
fmgr_fwobj_address:
ipv4: "iprange"
start_ip: "10.7.220.1"
end_ip: "10.7.220.125"
name: "ansible_range"
comment: "Created by Ansible"
- name: ADD IPv4 IP ADDRESS WILDCARD OBJECT
fmgr_fwobj_address:
ipv4: "wildcard"
wildcard: "10.7.220.30/255.255.255.255"
name: "ansible_wildcard"
comment: "Created by Ansible"
- name: ADD IPv4 IP ADDRESS WILDCARD FQDN OBJECT
fmgr_fwobj_address:
ipv4: "wildcard-fqdn"
wildcard_fqdn: "*.myds.com"
name: "Synology myds DDNS service"
comment: "Created by Ansible"
- name: ADD IPv4 IP ADDRESS FQDN OBJECT
fmgr_fwobj_address:
ipv4: "fqdn"
fqdn: "ansible.com"
name: "ansible_fqdn"
comment: "Created by Ansible"
- name: ADD IPv4 IP ADDRESS GEO OBJECT
fmgr_fwobj_address:
ipv4: "geography"
country: "usa"
name: "ansible_geo"
comment: "Created by Ansible"
- name: ADD IPv6 ADDRESS
fmgr_fwobj_address:
ipv6: "ip"
ipv6addr: "2001:0db8:85a3:0000:0000:8a2e:0370:7334"
name: "ansible_v6Obj"
comment: "Created by Ansible"
- name: ADD IPv6 ADDRESS RANGE
fmgr_fwobj_address:
ipv6: "iprange"
start_ip: "2001:0db8:85a3:0000:0000:8a2e:0370:7334"
end_ip: "2001:0db8:85a3:0000:0000:8a2e:0370:7446"
name: "ansible_v6range"
comment: "Created by Ansible"
- name: ADD IPv4 IP ADDRESS GROUP
fmgr_fwobj_address:
ipv4: "group"
group_name: "ansibleIPv4Group"
group_members: "ansible_fqdn, ansible_wildcard, ansible_range"
- name: ADD IPv6 IP ADDRESS GROUP
fmgr_fwobj_address:
ipv6: "group"
group_name: "ansibleIPv6Group"
group_members: "ansible_v6Obj, ansible_v6range"
- name: ADD MULTICAST RANGE
fmgr_fwobj_address:
multicast: "multicastrange"
start_ip: "224.0.0.251"
end_ip: "224.0.0.251"
name: "ansible_multicastrange"
comment: "Created by Ansible"
- name: ADD BROADCAST SUBNET
fmgr_fwobj_address:
multicast: "broadcastmask"
ipv4addr: "10.7.220.0/24"
name: "ansible_broadcastSubnet"
comment: "Created by Ansible"
'''
RETURN = """
api_result:
description: full API response, includes status code and message
returned: always
type: str
"""
import re
from ansible.module_utils.basic import AnsibleModule, env_fallback
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
def fmgr_fwobj_ipv4(fmgr, paramgram):
"""
:param fmgr: The fmgr object instance from fortimanager.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
"""
# EVAL THE MODE PARAMETER FOR SET OR ADD
if paramgram["mode"] in ['set', 'add']:
# CREATE THE DATAGRAM DICTIONARY
# ENSURE THE DATAGRAM KEYS MATCH THE JSON API GUIDE ATTRIBUTES, NOT WHAT IS IN ANSIBLE
# SOME PARAMETERS SHOWN IN THIS DICTIONARY WE DON'T EVEN ASK THE USER FOR IN PLAYBOOKS BUT ARE REQUIRED
datagram = {
"comment": paramgram["comment"],
"associated-interface": paramgram["associated-interface"],
"cache-ttl": paramgram["cache-ttl"],
"name": paramgram["name"],
"allow-routing": paramgram["allow-routing"],
"color": paramgram["color"],
"meta fields": {},
"dynamic_mapping": [],
"visibility": paramgram["allow-routing"],
"type": paramgram["ipv4"],
}
# SET THE CORRECT URL BASED ON THE TYPE (WE'RE DOING GROUPS IN THIS METHOD, TOO)
if datagram["type"] == "group":
url = '/pm/config/adom/{adom}/obj/firewall/addrgrp'.format(adom=paramgram["adom"])
else:
url = '/pm/config/adom/{adom}/obj/firewall/address'.format(adom=paramgram["adom"])
#########################
# IF type = 'ipmask'
#########################
if datagram["type"] == "ipmask":
# CREATE THE SUBNET LIST OBJECT
subnet = []
# EVAL THE IPV4ADDR INPUT AND SPLIT THE IP ADDRESS FROM THE MASK AND APPEND THEM TO THE SUBNET LIST
for subnets in paramgram["ipv4addr"].split("/"):
subnet.append(subnets)
# CHECK THAT THE SECOND ENTRY IN THE SUBNET LIST (WHAT WAS TO THE RIGHT OF THE / CHARACTER)
# IS IN SUBNET MASK FORMAT AND NOT CIDR FORMAT.
# IF IT IS IN CIDR FORMAT, WE NEED TO CONVERT IT TO SUBNET BIT MASK FORMAT FOR THE JSON API
if not re.match(r'\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}', subnet[1]):
# IF THE SUBNET PARAMETER INPUT DIDN'T LOOK LIKE xxx.xxx.xxx.xxx TO REGEX...
# ... RUN IT THROUGH THE CIDR_TO_NETMASK() FUNCTION
mask = fmgr._tools.cidr_to_netmask(subnet[1])
# AND THEN UPDATE THE SUBNET LIST OBJECT
subnet[1] = mask
# INCLUDE THE SUBNET LIST OBJECT IN THE DATAGRAM DICTIONARY TO BE SUBMITTED
datagram["subnet"] = subnet
#########################
# IF type = 'iprange'
#########################
if datagram["type"] == "iprange":
datagram["start-ip"] = paramgram["start-ip"]
datagram["end-ip"] = paramgram["end-ip"]
datagram["subnet"] = ["0.0.0.0", "0.0.0.0"]
#########################
# IF type = 'geography'
#########################
if datagram["type"] == "geography":
datagram["country"] = paramgram["country"]
#########################
# IF type = 'wildcard'
#########################
if datagram["type"] == "wildcard":
subnet = []
for subnets in paramgram["wildcard"].split("/"):
subnet.append(subnets)
if not re.match(r'\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}', subnet[1]):
mask = fmgr._tools.cidr_to_netmask(subnet[1])
subnet[1] = mask
datagram["wildcard"] = subnet
#########################
# IF type = 'wildcard-fqdn'
#########################
if datagram["type"] == "wildcard-fqdn":
datagram["wildcard-fqdn"] = paramgram["wildcard-fqdn"]
#########################
# IF type = 'fqdn'
#########################
if datagram["type"] == "fqdn":
datagram["fqdn"] = paramgram["fqdn"]
#########################
# IF type = 'group'
#########################
if datagram["type"] == "group":
datagram = {
"comment": paramgram["comment"],
"name": paramgram["group_name"],
"color": paramgram["color"],
"meta fields": {},
"dynamic_mapping": [],
"visibility": paramgram["visibility"]
}
members = []
group_members = paramgram["group_members"].replace(" ", "")
try:
for member in group_members.split(","):
members.append(member)
except Exception:
pass
datagram["member"] = members
# EVAL THE MODE PARAMETER FOR DELETE
if paramgram["mode"] == "delete":
# IF A GROUP, SET THE CORRECT NAME AND URL FOR THE GROUP ENDPOINT
if paramgram["ipv4"] == "group":
datagram = {}
url = '/pm/config/adom/{adom}/obj/firewall/addrgrp/{name}'.format(adom=paramgram["adom"],
name=paramgram["group_name"])
# OTHERWISE WE'RE JUST GOING TO USE THE ADDRESS ENDPOINT
else:
datagram = {}
url = '/pm/config/adom/{adom}/obj/firewall/address/{name}'.format(adom=paramgram["adom"],
name=paramgram["name"])
response = fmgr.process_request(url, datagram, paramgram["mode"])
return response
def fmgr_fwobj_ipv6(fmgr, paramgram):
"""
:param fmgr: The fmgr object instance from fortimanager.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
"""
# EVAL THE MODE PARAMETER FOR SET OR ADD
if paramgram["mode"] in ['set', 'add']:
# CREATE THE DATAGRAM DICTIONARY
# ENSURE THE DATAGRAM KEYS MATCH THE JSON API GUIDE ATTRIBUTES, NOT WHAT IS IN ANSIBLE
# SOME PARAMETERS SHOWN IN THIS DICTIONARY WE DON'T EVEN ASK THE USER FOR IN PLAYBOOKS BUT ARE REQUIRED
datagram = {
"comment": paramgram["comment"],
"name": paramgram["name"],
"color": paramgram["color"],
"dynamic_mapping": [],
"visibility": paramgram["visibility"],
"type": paramgram["ipv6"]
}
# SET THE CORRECT URL BASED ON THE TYPE (WE'RE DOING GROUPS IN THIS METHOD, TOO)
if datagram["type"] == "group":
url = '/pm/config/adom/{adom}/obj/firewall/addrgrp6'.format(adom=paramgram["adom"])
else:
url = '/pm/config/adom/{adom}/obj/firewall/address6'.format(adom=paramgram["adom"])
#########################
# IF type = 'ip'
#########################
if datagram["type"] == "ip":
datagram["type"] = "ipprefix"
datagram["ip6"] = paramgram["ipv6addr"]
#########################
# IF type = 'iprange'
#########################
if datagram["type"] == "iprange":
datagram["start-ip"] = paramgram["start-ip"]
datagram["end-ip"] = paramgram["end-ip"]
#########################
# IF type = 'group'
#########################
if datagram["type"] == "group":
datagram = None
datagram = {
"comment": paramgram["comment"],
"name": paramgram["group_name"],
"color": paramgram["color"],
"visibility": paramgram["visibility"]
}
members = []
group_members = paramgram["group_members"].replace(" ", "")
try:
for member in group_members.split(","):
members.append(member)
except Exception:
pass
datagram["member"] = members
# EVAL THE MODE PARAMETER FOR DELETE
if paramgram["mode"] == "delete":
# IF A GROUP, SET THE CORRECT NAME AND URL FOR THE GROUP ENDPOINT
if paramgram["ipv6"] == "group":
datagram = {}
url = '/pm/config/adom/{adom}/obj/firewall/addrgrp6/{name}'.format(adom=paramgram["adom"],
name=paramgram["group_name"])
# OTHERWISE WE'RE JUST GOING TO USE THE ADDRESS ENDPOINT
else:
datagram = {}
url = '/pm/config/adom/{adom}/obj/firewall/address6/{name}'.format(adom=paramgram["adom"],
name=paramgram["name"])
response = fmgr.process_request(url, datagram, paramgram["mode"])
return response
def fmgr_fwobj_multicast(fmgr, paramgram):
"""
:param fmgr: The fmgr object instance from fortimanager.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
"""
# EVAL THE MODE PARAMETER FOR SET OR ADD
if paramgram["mode"] in ['set', 'add']:
# CREATE THE DATAGRAM DICTIONARY
# ENSURE THE DATAGRAM KEYS MATCH THE JSON API GUIDE ATTRIBUTES, NOT WHAT IS IN ANSIBLE
# SOME PARAMETERS SHOWN IN THIS DICTIONARY WE DON'T EVEN ASK THE USER FOR IN PLAYBOOKS BUT ARE REQUIRED
datagram = {
"associated-interface": paramgram["associated-interface"],
"comment": paramgram["comment"],
"name": paramgram["name"],
"color": paramgram["color"],
"type": paramgram["multicast"],
"visibility": paramgram["visibility"],
}
# SET THE CORRECT URL
url = '/pm/config/adom/{adom}/obj/firewall/multicast-address'.format(adom=paramgram["adom"])
#########################
# IF type = 'multicastrange'
#########################
if paramgram["multicast"] == "multicastrange":
datagram["start-ip"] = paramgram["start-ip"]
datagram["end-ip"] = paramgram["end-ip"]
datagram["subnet"] = ["0.0.0.0", "0.0.0.0"]
#########################
# IF type = 'broadcastmask'
#########################
if paramgram["multicast"] == "broadcastmask":
# EVAL THE IPV4ADDR INPUT AND SPLIT THE IP ADDRESS FROM THE MASK AND APPEND THEM TO THE SUBNET LIST
subnet = []
for subnets in paramgram["ipv4addr"].split("/"):
subnet.append(subnets)
# CHECK THAT THE SECOND ENTRY IN THE SUBNET LIST (WHAT WAS TO THE RIGHT OF THE / CHARACTER)
# IS IN SUBNET MASK FORMAT AND NOT CIDR FORMAT.
# IF IT IS IN CIDR FORMAT, WE NEED TO CONVERT IT TO SUBNET BIT MASK FORMAT FOR THE JSON API
if not re.match(r'\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}', subnet[1]):
# IF THE SUBNET PARAMETER INPUT DIDN'T LOOK LIKE 255.255.255.255 TO REGEX...
# ... RUN IT THROUGH THE fmgr_cidr_to_netmask() FUNCTION
mask = fmgr._tools.cidr_to_netmask(subnet[1])
# AND THEN UPDATE THE SUBNET LIST OBJECT
subnet[1] = mask
# INCLUDE THE SUBNET LIST OBJECT IN THE DATAGRAM DICTIONARY TO BE SUBMITTED
datagram["subnet"] = subnet
# EVAL THE MODE PARAMETER FOR DELETE
if paramgram["mode"] == "delete":
datagram = {
"name": paramgram["name"]
}
# SET THE CORRECT URL FOR DELETE
url = '/pm/config/adom/{adom}/obj/firewall/multicast-address/{name}'.format(adom=paramgram["adom"],
name=paramgram["name"])
response = fmgr.process_request(url, datagram, paramgram["mode"])
return response
def main():
argument_spec = dict(
adom=dict(required=False, type="str", default="root"),
mode=dict(choices=["add", "set", "delete"], type="str", default="add"),
allow_routing=dict(required=False, type="str", choices=['enable', 'disable'], default="disable"),
associated_interface=dict(required=False, type="str"),
cache_ttl=dict(required=False, type="str"),
color=dict(required=False, type="str", default=22),
comment=dict(required=False, type="str"),
country=dict(required=False, type="str"),
fqdn=dict(required=False, type="str"),
name=dict(required=False, type="str"),
start_ip=dict(required=False, type="str"),
end_ip=dict(required=False, type="str"),
ipv4=dict(required=False, type="str", choices=['ipmask', 'iprange', 'fqdn', 'wildcard',
'geography', 'wildcard-fqdn', 'group']),
visibility=dict(required=False, type="str", choices=['enable', 'disable'], default="enable"),
wildcard=dict(required=False, type="str"),
wildcard_fqdn=dict(required=False, type="str"),
ipv6=dict(required=False, type="str", choices=['ip', 'iprange', 'group']),
group_members=dict(required=False, type="str"),
group_name=dict(required=False, type="str"),
ipv4addr=dict(required=False, type="str"),
ipv6addr=dict(required=False, type="str"),
multicast=dict(required=False, type="str", choices=['multicastrange', 'broadcastmask', 'ip6']),
obj_id=dict(required=False, type="str"),
)
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False,
mutually_exclusive=[
['ipv4', 'ipv6'],
['ipv4', 'multicast'],
['ipv6', 'multicast']
])
paramgram = {
"adom": module.params["adom"],
"allow-routing": module.params["allow_routing"],
"associated-interface": module.params["associated_interface"],
"cache-ttl": module.params["cache_ttl"],
"color": module.params["color"],
"comment": module.params["comment"],
"country": module.params["country"],
"end-ip": module.params["end_ip"],
"fqdn": module.params["fqdn"],
"name": module.params["name"],
"start-ip": module.params["start_ip"],
"visibility": module.params["visibility"],
"wildcard": module.params["wildcard"],
"wildcard-fqdn": module.params["wildcard_fqdn"],
"ipv6": module.params["ipv6"],
"ipv4": module.params["ipv4"],
"group_members": module.params["group_members"],
"group_name": module.params["group_name"],
"ipv4addr": module.params["ipv4addr"],
"ipv6addr": module.params["ipv6addr"],
"multicast": module.params["multicast"],
"mode": module.params["mode"],
"obj-id": module.params["obj_id"],
}
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)
results = DEFAULT_RESULT_OBJ
try:
if paramgram["ipv4"]:
results = fmgr_fwobj_ipv4(fmgr, paramgram)
elif paramgram["ipv6"]:
results = fmgr_fwobj_ipv6(fmgr, paramgram)
elif paramgram["multicast"]:
results = fmgr_fwobj_multicast(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)
if results is not None:
return module.exit_json(**results[1])
else:
return module.exit_json(msg="Couldn't find a proper ipv4 or ipv6 or multicast parameter "
"to run in the logic tree. Exiting...")
if __name__ == "__main__":
main()