mirror of https://github.com/ansible/ansible.git
Panos object (#28028)
* - deprecated panos_address and panos_service in lieu of common panos_object * - deprecated/removed panos_address and panos_service in lieu for panos_object * squash! - deprecated/removed panos_address and panos_service in lieu for panos_object * - fixed PEP8 issues * - ansible_metadata requires metadata_version instead of just version key in 2.4 * add > to multi line descriptions * update version string to 2.4 * Update legacy-files.txtpull/28031/head
parent
08767946cb
commit
8b617aaef5
@ -1,216 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# Ansible module to manage PaloAltoNetworks Firewall
|
|
||||||
# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
|
|
||||||
#
|
|
||||||
# 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/>.
|
|
||||||
|
|
||||||
ANSIBLE_METADATA = {'metadata_version': '1.0',
|
|
||||||
'status': ['preview'],
|
|
||||||
'supported_by': 'community'}
|
|
||||||
|
|
||||||
|
|
||||||
DOCUMENTATION = '''
|
|
||||||
---
|
|
||||||
module: panos_address
|
|
||||||
short_description: Create address service object on PanOS devices
|
|
||||||
description:
|
|
||||||
- Create address service object of different types [IP Range, FQDN, or IP Netmask].
|
|
||||||
author: "Luigi Mori (@jtschichold), Ken Celenza (@itdependsnetworks), Ivan Bojer (@ivanbojer)"
|
|
||||||
version_added: "2.3"
|
|
||||||
requirements:
|
|
||||||
- pan-python can be obtained from PyPi U(https://pypi.python.org/pypi/pan-python)
|
|
||||||
options:
|
|
||||||
ip_address:
|
|
||||||
description:
|
|
||||||
- IP address (or hostname) of PAN-OS device being configured.
|
|
||||||
required: true
|
|
||||||
username:
|
|
||||||
description:
|
|
||||||
- Username credentials to use for authentication.
|
|
||||||
default: "admin"
|
|
||||||
password:
|
|
||||||
description:
|
|
||||||
- Password credentials to use for authentication.
|
|
||||||
required: true
|
|
||||||
address:
|
|
||||||
description:
|
|
||||||
- IP address with or without mask, range, or FQDN.
|
|
||||||
required: true
|
|
||||||
default: None
|
|
||||||
address_name:
|
|
||||||
description:
|
|
||||||
- Human readable name of the address.
|
|
||||||
required: true
|
|
||||||
default: None
|
|
||||||
type:
|
|
||||||
description:
|
|
||||||
- This is the type of the object created.
|
|
||||||
default: ip-nemask
|
|
||||||
choices: [ 'ip-netmask', 'fqdn', 'ip-range' ]
|
|
||||||
description:
|
|
||||||
description:
|
|
||||||
- Description of the address object.
|
|
||||||
default: None
|
|
||||||
tag:
|
|
||||||
description:
|
|
||||||
- Tag of the address object.
|
|
||||||
default: None
|
|
||||||
commit:
|
|
||||||
description:
|
|
||||||
- Commit configuration to the Firewall if it is changed.
|
|
||||||
default: true
|
|
||||||
'''
|
|
||||||
|
|
||||||
EXAMPLES = '''
|
|
||||||
- name: create IP-Netmask Object
|
|
||||||
panos_address:
|
|
||||||
ip_address: "192.168.1.1"
|
|
||||||
password: 'admin'
|
|
||||||
address_name: 'google_dns'
|
|
||||||
address: '8.8.8.8/32'
|
|
||||||
description: 'Google DNS'
|
|
||||||
tag: 'Outbound'
|
|
||||||
commit: False
|
|
||||||
|
|
||||||
- name: create IP-Range Object
|
|
||||||
panos_address:
|
|
||||||
ip_address: "192.168.1.1"
|
|
||||||
password: 'admin'
|
|
||||||
type: 'ip-range'
|
|
||||||
address_name: 'apple-range'
|
|
||||||
address: '17.0.0.0-17.255.255.255'
|
|
||||||
commit: False
|
|
||||||
|
|
||||||
- name: create FQDN Object
|
|
||||||
panos_address:
|
|
||||||
ip_address: "192.168.1.1"
|
|
||||||
password: 'admin'
|
|
||||||
type: 'fqdn'
|
|
||||||
address_name: 'google.com'
|
|
||||||
address: 'www.google.com'
|
|
||||||
'''
|
|
||||||
|
|
||||||
RETURN = '''
|
|
||||||
# Default return values
|
|
||||||
'''
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
|
|
||||||
try:
|
|
||||||
import pan.xapi
|
|
||||||
from pan.xapi import PanXapiError
|
|
||||||
|
|
||||||
HAS_LIB = True
|
|
||||||
except ImportError:
|
|
||||||
HAS_LIB = False
|
|
||||||
|
|
||||||
_ADDRESS_XPATH = "/config/devices/entry[@name='localhost.localdomain']" + \
|
|
||||||
"/vsys/entry[@name='vsys1']" + \
|
|
||||||
"/address/entry[@name='%s']"
|
|
||||||
|
|
||||||
|
|
||||||
def address_exists(xapi, address_name):
|
|
||||||
xapi.get(_ADDRESS_XPATH % address_name)
|
|
||||||
e = xapi.element_root.find('.//entry')
|
|
||||||
if e is None:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def add_address(xapi, module, address, address_name, description, type, tag):
|
|
||||||
if address_exists(xapi, address_name):
|
|
||||||
return False
|
|
||||||
|
|
||||||
exml = []
|
|
||||||
exml.append('<%s>' % type)
|
|
||||||
exml.append('%s' % address)
|
|
||||||
exml.append('</%s>' % type)
|
|
||||||
|
|
||||||
if description:
|
|
||||||
exml.append('<description>')
|
|
||||||
exml.append('%s' % description)
|
|
||||||
exml.append('</description>')
|
|
||||||
|
|
||||||
if tag:
|
|
||||||
exml.append('<tag>')
|
|
||||||
exml.append('<member>%s</member>' % tag)
|
|
||||||
exml.append('</tag>')
|
|
||||||
|
|
||||||
exml = ''.join(exml)
|
|
||||||
|
|
||||||
xapi.set(xpath=_ADDRESS_XPATH % address_name, element=exml)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
argument_spec = dict(
|
|
||||||
ip_address=dict(required=True),
|
|
||||||
password=dict(required=True, no_log=True),
|
|
||||||
username=dict(default='admin'),
|
|
||||||
address_name=dict(required=True),
|
|
||||||
address=dict(),
|
|
||||||
description=dict(),
|
|
||||||
tag=dict(),
|
|
||||||
type=dict(default='ip-netmask', choices=['ip-netmask', 'ip-range', 'fqdn']),
|
|
||||||
commit=dict(type='bool', default=True)
|
|
||||||
)
|
|
||||||
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False)
|
|
||||||
|
|
||||||
if not HAS_LIB:
|
|
||||||
module.fail_json(msg='pan-python required for this module')
|
|
||||||
|
|
||||||
ip_address = module.params["ip_address"]
|
|
||||||
password = module.params["password"]
|
|
||||||
username = module.params['username']
|
|
||||||
|
|
||||||
xapi = pan.xapi.PanXapi(
|
|
||||||
hostname=ip_address,
|
|
||||||
api_username=username,
|
|
||||||
api_password=password
|
|
||||||
)
|
|
||||||
|
|
||||||
address_name = module.params['address_name']
|
|
||||||
address = module.params['address']
|
|
||||||
commit = module.params['commit']
|
|
||||||
|
|
||||||
description = module.params['description']
|
|
||||||
tag = module.params['tag']
|
|
||||||
type = module.params['type']
|
|
||||||
|
|
||||||
changed = False
|
|
||||||
try:
|
|
||||||
changed = add_address(xapi, module,
|
|
||||||
address,
|
|
||||||
address_name,
|
|
||||||
description,
|
|
||||||
type,
|
|
||||||
tag)
|
|
||||||
except PanXapiError:
|
|
||||||
exc = get_exception()
|
|
||||||
module.fail_json(msg=exc.message)
|
|
||||||
|
|
||||||
if changed and commit:
|
|
||||||
xapi.commit(cmd="<commit></commit>", sync=True, interval=1)
|
|
||||||
|
|
||||||
module.exit_json(changed=changed, msg="okey dokey")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -0,0 +1,463 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Ansible module to manage PaloAltoNetworks Firewall
|
||||||
|
# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
|
||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
ANSIBLE_METADATA = {'status': ['preview'],
|
||||||
|
'supported_by': 'community',
|
||||||
|
'metadata_version': '1.0'}
|
||||||
|
|
||||||
|
DOCUMENTATION = '''
|
||||||
|
---
|
||||||
|
module: panos_object
|
||||||
|
short_description: create/read/update/delete object in PAN-OS or Panorama
|
||||||
|
description: >
|
||||||
|
- Policy objects form the match criteria for policy rules and many other functions in PAN-OS. These may include
|
||||||
|
address object, address groups, service objects, service groups, and tag.
|
||||||
|
author: "Bob Hagen (@rnh556)"
|
||||||
|
version_added: "2.4"
|
||||||
|
requirements:
|
||||||
|
- pan-python can be obtained from PyPi U(https://pypi.python.org/pypi/pan-python)
|
||||||
|
- pandevice can be obtained from PyPi U(https://pypi.python.org/pypi/pandevice)
|
||||||
|
notes:
|
||||||
|
- Checkmode is not supported.
|
||||||
|
- Panorama is supported.
|
||||||
|
options:
|
||||||
|
ip_address:
|
||||||
|
description:
|
||||||
|
- IP address (or hostname) of PAN-OS device or Panorama management console being configured.
|
||||||
|
required: true
|
||||||
|
username:
|
||||||
|
description:
|
||||||
|
- Username credentials to use for authentication.
|
||||||
|
required: false
|
||||||
|
default: "admin"
|
||||||
|
password:
|
||||||
|
description:
|
||||||
|
- Password credentials to use for authentication.
|
||||||
|
required: true
|
||||||
|
api_key:
|
||||||
|
description:
|
||||||
|
- API key that can be used instead of I(username)/I(password) credentials.
|
||||||
|
operation:
|
||||||
|
description:
|
||||||
|
- The operation to be performed. Supported values are I(add)/I(delete)/I(find).
|
||||||
|
required: true
|
||||||
|
addressobject:
|
||||||
|
description:
|
||||||
|
- The name of the address object.
|
||||||
|
address:
|
||||||
|
description:
|
||||||
|
- The IP address of the host or network in CIDR notation.
|
||||||
|
address_type:
|
||||||
|
description:
|
||||||
|
- The type of address object definition. Valid types are I(ip-netmask) and I(ip-range).
|
||||||
|
addressgroup:
|
||||||
|
description:
|
||||||
|
- A static group of address objects or dynamic address group.
|
||||||
|
static_value:
|
||||||
|
description:
|
||||||
|
- A group of address objects to be used in an addressgroup definition.
|
||||||
|
dynamic_value:
|
||||||
|
description:
|
||||||
|
- The filter match criteria to be used in a dynamic addressgroup definition.
|
||||||
|
serviceobject:
|
||||||
|
description:
|
||||||
|
- The name of the service object.
|
||||||
|
source_port:
|
||||||
|
description:
|
||||||
|
- The source port to be used in a service object definition.
|
||||||
|
destination_port:
|
||||||
|
description:
|
||||||
|
- The destination port to be used in a service object definition.
|
||||||
|
protocol:
|
||||||
|
description:
|
||||||
|
- The IP protocol to be used in a service object definition. Valid values are I(tcp) or I(udp).
|
||||||
|
servicegroup:
|
||||||
|
description:
|
||||||
|
- A group of service objects.
|
||||||
|
services:
|
||||||
|
description:
|
||||||
|
- The group of service objects used in a servicegroup definition.
|
||||||
|
description:
|
||||||
|
description:
|
||||||
|
- The description of the object.
|
||||||
|
tag_name:
|
||||||
|
description:
|
||||||
|
- The name of an object or rule tag.
|
||||||
|
color:
|
||||||
|
description: >
|
||||||
|
- The color of the tag object. Valid values are I(red, green, blue, yellow, copper, orange, purple, gray,
|
||||||
|
light green, cyan, light gray, blue gray, lime, black, gold, and brown).
|
||||||
|
devicegroup:
|
||||||
|
description: >
|
||||||
|
- The name of the Panorama device group. The group must exist on Panorama. If device group is not defined it
|
||||||
|
is assumed that we are contacting a firewall.
|
||||||
|
required: false
|
||||||
|
default: None
|
||||||
|
'''
|
||||||
|
|
||||||
|
EXAMPLES = '''
|
||||||
|
- name: search for shared address object
|
||||||
|
panos_object:
|
||||||
|
ip_address: '{{ ip_address }}'
|
||||||
|
username: '{{ username }}'
|
||||||
|
password: '{{ password }}'
|
||||||
|
operation: 'find'
|
||||||
|
address: 'DevNet'
|
||||||
|
|
||||||
|
- name: create an address group in devicegroup using API key
|
||||||
|
panos_object:
|
||||||
|
ip_address: '{{ ip_address }}'
|
||||||
|
api_key: '{{ api_key }}'
|
||||||
|
operation: 'add'
|
||||||
|
addressgroup: 'Prod_DB_Svrs'
|
||||||
|
static_value: ['prod-db1', 'prod-db2', 'prod-db3']
|
||||||
|
description: 'Production DMZ database servers'
|
||||||
|
tag_name: 'DMZ'
|
||||||
|
devicegroup: 'DMZ Firewalls'
|
||||||
|
|
||||||
|
- name: create a global service for TCP 3306
|
||||||
|
panos_object:
|
||||||
|
ip_address: '{{ ip_address }}'
|
||||||
|
api_key: '{{ api_key }}'
|
||||||
|
operation: 'add'
|
||||||
|
serviceobject: 'mysql-3306'
|
||||||
|
destination_port: '3306'
|
||||||
|
protocol: 'tcp'
|
||||||
|
description: 'MySQL on tcp/3306'
|
||||||
|
|
||||||
|
- name: create a global tag
|
||||||
|
panos_object:
|
||||||
|
ip_address: '{{ ip_address }}'
|
||||||
|
username: '{{ username }}'
|
||||||
|
password: '{{ password }}'
|
||||||
|
operation: 'add'
|
||||||
|
tag_name: 'ProjectX'
|
||||||
|
color: 'yellow'
|
||||||
|
description: 'Associated with Project X'
|
||||||
|
|
||||||
|
- name: delete an address object from a devicegroup using API key
|
||||||
|
panos_object:
|
||||||
|
ip_address: '{{ ip_address }}'
|
||||||
|
api_key: '{{ api_key }}'
|
||||||
|
operation: 'delete'
|
||||||
|
addressobject: 'Win2K test'
|
||||||
|
'''
|
||||||
|
|
||||||
|
RETURN = '''
|
||||||
|
# Default return values
|
||||||
|
'''
|
||||||
|
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.basic import get_exception
|
||||||
|
|
||||||
|
try:
|
||||||
|
import pan.xapi
|
||||||
|
from pan.xapi import PanXapiError
|
||||||
|
import pandevice
|
||||||
|
from pandevice import base
|
||||||
|
from pandevice import firewall
|
||||||
|
from pandevice import panorama
|
||||||
|
from pandevice import objects
|
||||||
|
import xmltodict
|
||||||
|
import json
|
||||||
|
|
||||||
|
HAS_LIB = True
|
||||||
|
except ImportError:
|
||||||
|
HAS_LIB = False
|
||||||
|
|
||||||
|
|
||||||
|
def get_devicegroup(device, devicegroup):
|
||||||
|
dg_list = device.refresh_devices()
|
||||||
|
for group in dg_list:
|
||||||
|
if isinstance(group, pandevice.panorama.DeviceGroup):
|
||||||
|
if group.name == devicegroup:
|
||||||
|
return group
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def find_object(device, dev_group, obj_name, obj_type):
|
||||||
|
# Get the firewall objects
|
||||||
|
obj_type.refreshall(device)
|
||||||
|
if isinstance(device, pandevice.firewall.Firewall):
|
||||||
|
addr = device.find(obj_name, obj_type)
|
||||||
|
return addr
|
||||||
|
elif isinstance(device, pandevice.panorama.Panorama):
|
||||||
|
addr = device.find(obj_name, obj_type)
|
||||||
|
if addr is None:
|
||||||
|
if dev_group:
|
||||||
|
device.add(dev_group)
|
||||||
|
obj_type.refreshall(dev_group)
|
||||||
|
addr = dev_group.find(obj_name, obj_type)
|
||||||
|
return addr
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def create_object(**kwargs):
|
||||||
|
if kwargs['addressobject']:
|
||||||
|
newobject = objects.AddressObject(
|
||||||
|
name=kwargs['addressobject'],
|
||||||
|
value=kwargs['address'],
|
||||||
|
type=kwargs['address_type'],
|
||||||
|
description=kwargs['description'],
|
||||||
|
tag=kwargs['tag_name']
|
||||||
|
)
|
||||||
|
if newobject.type and newobject.value:
|
||||||
|
return newobject
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
elif kwargs['addressgroup']:
|
||||||
|
newobject = objects.AddressGroup(
|
||||||
|
name=kwargs['addressgroup'],
|
||||||
|
static_value=kwargs['static_value'],
|
||||||
|
dynamic_value=kwargs['dynamic_value'],
|
||||||
|
description=kwargs['description'],
|
||||||
|
tag=kwargs['tag_name']
|
||||||
|
)
|
||||||
|
if newobject.static_value or newobject.dynamic_value:
|
||||||
|
return newobject
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
elif kwargs['serviceobject']:
|
||||||
|
newobject = objects.ServiceObject(
|
||||||
|
name=kwargs['serviceobject'],
|
||||||
|
protocol=kwargs['protocol'],
|
||||||
|
source_port=kwargs['source_port'],
|
||||||
|
destination_port=kwargs['destination_port'],
|
||||||
|
tag=kwargs['tag_name']
|
||||||
|
)
|
||||||
|
if newobject.protocol and newobject.destination_port:
|
||||||
|
return newobject
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
elif kwargs['servicegroup']:
|
||||||
|
newobject = objects.ServiceGroup(
|
||||||
|
name=kwargs['servicegroup'],
|
||||||
|
value=kwargs['services'],
|
||||||
|
tag=kwargs['tag_name']
|
||||||
|
)
|
||||||
|
if newobject.value:
|
||||||
|
return newobject
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
elif kwargs['tag_name']:
|
||||||
|
newobject = objects.Tag(
|
||||||
|
name=kwargs['tag_name'],
|
||||||
|
color=kwargs['color'],
|
||||||
|
comments=kwargs['description']
|
||||||
|
)
|
||||||
|
if newobject.name:
|
||||||
|
return newobject
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def add_object(device, dev_group, new_object):
|
||||||
|
if dev_group:
|
||||||
|
dev_group.add(new_object)
|
||||||
|
else:
|
||||||
|
device.add(new_object)
|
||||||
|
new_object.create()
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
argument_spec = dict(
|
||||||
|
ip_address=dict(required=True),
|
||||||
|
password=dict(no_log=True),
|
||||||
|
username=dict(default='admin'),
|
||||||
|
api_key=dict(no_log=True),
|
||||||
|
operation=dict(required=True, choices=['add', 'update', 'delete', 'find']),
|
||||||
|
addressobject=dict(default=None),
|
||||||
|
addressgroup=dict(default=None),
|
||||||
|
serviceobject=dict(default=None),
|
||||||
|
servicegroup=dict(default=None),
|
||||||
|
address=dict(default=None),
|
||||||
|
address_type=dict(default='ip-netmask', choices=['ip-netmask', 'ip-range', 'fqdn']),
|
||||||
|
static_value=dict(type='list', default=None),
|
||||||
|
dynamic_value=dict(default=None),
|
||||||
|
protocol=dict(default=None, choices=['tcp', 'udp']),
|
||||||
|
source_port=dict(default=None),
|
||||||
|
destination_port=dict(default=None),
|
||||||
|
services=dict(type='list', default=None),
|
||||||
|
description=dict(default=None),
|
||||||
|
tag_name=dict(default=None),
|
||||||
|
color=dict(default=None, choices=['red', 'green', 'blue', 'yellow', 'copper', 'orange', 'purple',
|
||||||
|
'gray', 'light green', 'cyan', 'light gray', 'blue gray',
|
||||||
|
'lime', 'black', 'gold', 'brown']),
|
||||||
|
devicegroup=dict(default=None)
|
||||||
|
)
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False,
|
||||||
|
required_one_of=[['api_key', 'password']],
|
||||||
|
mutually_exclusive=[['addressobject', 'addressgroup',
|
||||||
|
'serviceobject', 'servicegroup',
|
||||||
|
'tag_name']]
|
||||||
|
)
|
||||||
|
if not HAS_LIB:
|
||||||
|
module.fail_json(msg='Missing required libraries.')
|
||||||
|
|
||||||
|
ip_address = module.params["ip_address"]
|
||||||
|
password = module.params["password"]
|
||||||
|
username = module.params['username']
|
||||||
|
api_key = module.params['api_key']
|
||||||
|
operation = module.params['operation']
|
||||||
|
addressobject = module.params['addressobject']
|
||||||
|
addressgroup = module.params['addressgroup']
|
||||||
|
serviceobject = module.params['serviceobject']
|
||||||
|
servicegroup = module.params['servicegroup']
|
||||||
|
address = module.params['address']
|
||||||
|
address_type = module.params['address_type']
|
||||||
|
static_value = module.params['static_value']
|
||||||
|
dynamic_value = module.params['dynamic_value']
|
||||||
|
protocol = module.params['protocol']
|
||||||
|
source_port = module.params['source_port']
|
||||||
|
destination_port = module.params['destination_port']
|
||||||
|
services = module.params['services']
|
||||||
|
description = module.params['description']
|
||||||
|
tag_name = module.params['tag_name']
|
||||||
|
color = module.params['color']
|
||||||
|
devicegroup = module.params['devicegroup']
|
||||||
|
|
||||||
|
# Create the device with the appropriate pandevice type
|
||||||
|
device = base.PanDevice.create_from_device(ip_address, username, password, api_key=api_key)
|
||||||
|
|
||||||
|
# If Panorama, validate the devicegroup
|
||||||
|
dev_group = None
|
||||||
|
if devicegroup and isinstance(device, panorama.Panorama):
|
||||||
|
dev_group = get_devicegroup(device, devicegroup)
|
||||||
|
if dev_group:
|
||||||
|
device.add(dev_group)
|
||||||
|
else:
|
||||||
|
module.fail_json(msg='\'%s\' device group not found in Panorama. Is the name correct?' % devicegroup)
|
||||||
|
|
||||||
|
# What type of object are we talking about?
|
||||||
|
if addressobject:
|
||||||
|
obj_name = addressobject
|
||||||
|
obj_type = objects.AddressObject
|
||||||
|
elif addressgroup:
|
||||||
|
obj_name = addressgroup
|
||||||
|
obj_type = objects.AddressGroup
|
||||||
|
elif serviceobject:
|
||||||
|
obj_name = serviceobject
|
||||||
|
obj_type = objects.ServiceObject
|
||||||
|
elif servicegroup:
|
||||||
|
obj_name = servicegroup
|
||||||
|
obj_type = objects.ServiceGroup
|
||||||
|
elif tag_name:
|
||||||
|
obj_name = tag_name
|
||||||
|
obj_type = objects.Tag
|
||||||
|
else:
|
||||||
|
module.fail_json(msg='No object type defined!')
|
||||||
|
|
||||||
|
# Which operation shall we perform on the object?
|
||||||
|
if operation == "find":
|
||||||
|
# Search for the object
|
||||||
|
match = find_object(device, dev_group, obj_name, obj_type)
|
||||||
|
|
||||||
|
# If found, format and return the result
|
||||||
|
if match:
|
||||||
|
match_dict = xmltodict.parse(match.element_str())
|
||||||
|
module.exit_json(
|
||||||
|
stdout_lines=json.dumps(match_dict, indent=2),
|
||||||
|
msg='Object matched'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
module.fail_json(msg='Object \'%s\' not found. Is the name correct?' % obj_name)
|
||||||
|
elif operation == "delete":
|
||||||
|
# Search for the object
|
||||||
|
match = find_object(device, dev_group, obj_name, obj_type)
|
||||||
|
|
||||||
|
# If found, delete it
|
||||||
|
if match:
|
||||||
|
try:
|
||||||
|
match.delete()
|
||||||
|
except PanXapiError:
|
||||||
|
exc = get_exception()
|
||||||
|
module.fail_json(msg=exc.message)
|
||||||
|
|
||||||
|
module.exit_json(changed=True, msg='Object \'%s\' successfully deleted' % obj_name)
|
||||||
|
else:
|
||||||
|
module.fail_json(msg='Object \'%s\' not found. Is the name correct?' % obj_name)
|
||||||
|
elif operation == "add":
|
||||||
|
# Search for the object. Fail if found.
|
||||||
|
match = find_object(device, dev_group, obj_name, obj_type)
|
||||||
|
if match:
|
||||||
|
module.fail_json(msg='Object \'%s\' already exists. Use operation: \'update\' to change it.' % obj_name)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
new_object = create_object(
|
||||||
|
addressobject=addressobject,
|
||||||
|
addressgroup=addressgroup,
|
||||||
|
serviceobject=serviceobject,
|
||||||
|
servicegroup=servicegroup,
|
||||||
|
address=address,
|
||||||
|
address_type=address_type,
|
||||||
|
static_value=static_value,
|
||||||
|
dynamic_value=dynamic_value,
|
||||||
|
protocol=protocol,
|
||||||
|
source_port=source_port,
|
||||||
|
destination_port=destination_port,
|
||||||
|
services=services,
|
||||||
|
description=description,
|
||||||
|
tag_name=tag_name,
|
||||||
|
color=color
|
||||||
|
)
|
||||||
|
changed = add_object(device, dev_group, new_object)
|
||||||
|
except PanXapiError:
|
||||||
|
exc = get_exception()
|
||||||
|
module.fail_json(msg=exc.message)
|
||||||
|
module.exit_json(changed=changed, msg='Object \'%s\' successfully added' % obj_name)
|
||||||
|
elif operation == "update":
|
||||||
|
# Search for the object. Update if found.
|
||||||
|
match = find_object(device, dev_group, obj_name, obj_type)
|
||||||
|
if match:
|
||||||
|
try:
|
||||||
|
new_object = create_object(
|
||||||
|
addressobject=addressobject,
|
||||||
|
addressgroup=addressgroup,
|
||||||
|
serviceobject=serviceobject,
|
||||||
|
servicegroup=servicegroup,
|
||||||
|
address=address,
|
||||||
|
address_type=address_type,
|
||||||
|
static_value=static_value,
|
||||||
|
dynamic_value=dynamic_value,
|
||||||
|
protocol=protocol,
|
||||||
|
source_port=source_port,
|
||||||
|
destination_port=destination_port,
|
||||||
|
services=services,
|
||||||
|
description=description,
|
||||||
|
tag_name=tag_name,
|
||||||
|
color=color
|
||||||
|
)
|
||||||
|
changed = add_object(device, dev_group, new_object)
|
||||||
|
except PanXapiError:
|
||||||
|
exc = get_exception()
|
||||||
|
module.fail_json(msg=exc.message)
|
||||||
|
module.exit_json(changed=changed, msg='Object \'%s\' successfully updated.' % obj_name)
|
||||||
|
else:
|
||||||
|
module.fail_json(msg='Object \'%s\' does not exist. Use operation: \'add\' to add it.' % obj_name)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
@ -1,178 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# Ansible module to manage PaloAltoNetworks Firewall
|
|
||||||
# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
|
|
||||||
#
|
|
||||||
# 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/>.
|
|
||||||
|
|
||||||
DOCUMENTATION = '''
|
|
||||||
---
|
|
||||||
module: panos_service
|
|
||||||
short_description: create a service object
|
|
||||||
description:
|
|
||||||
- Create a service object. Service objects are fundamental representation of the applications given src/dst ports and protocol
|
|
||||||
author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)"
|
|
||||||
version_added: "2.3"
|
|
||||||
requirements:
|
|
||||||
- pan-python
|
|
||||||
options:
|
|
||||||
ip_address:
|
|
||||||
description:
|
|
||||||
- IP address (or hostname) of PAN-OS device
|
|
||||||
required: true
|
|
||||||
password:
|
|
||||||
description:
|
|
||||||
- password for authentication
|
|
||||||
required: true
|
|
||||||
username:
|
|
||||||
description:
|
|
||||||
- username for authentication
|
|
||||||
required: false
|
|
||||||
default: "admin"
|
|
||||||
service_name:
|
|
||||||
description:
|
|
||||||
- name of the service
|
|
||||||
required: true
|
|
||||||
protocol:
|
|
||||||
description:
|
|
||||||
- protocol for the service, should be tcp or udp
|
|
||||||
required: true
|
|
||||||
port:
|
|
||||||
description:
|
|
||||||
- destination port
|
|
||||||
required: true
|
|
||||||
source_port:
|
|
||||||
description:
|
|
||||||
- source port
|
|
||||||
required: false
|
|
||||||
default: None
|
|
||||||
commit:
|
|
||||||
description:
|
|
||||||
- commit if changed
|
|
||||||
required: false
|
|
||||||
default: true
|
|
||||||
'''
|
|
||||||
|
|
||||||
EXAMPLES = '''
|
|
||||||
# Creates service for port 22
|
|
||||||
- name: create SSH service
|
|
||||||
panos_service:
|
|
||||||
ip_address: "192.168.1.1"
|
|
||||||
password: "admin"
|
|
||||||
service_name: "service-tcp-22"
|
|
||||||
protocol: "tcp"
|
|
||||||
port: "22"
|
|
||||||
'''
|
|
||||||
|
|
||||||
RETURN='''
|
|
||||||
# Default return values
|
|
||||||
'''
|
|
||||||
|
|
||||||
ANSIBLE_METADATA = {'metadata_version': '1.0',
|
|
||||||
'status': ['preview'],
|
|
||||||
'supported_by': 'community'}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
|
|
||||||
try:
|
|
||||||
import pan.xapi
|
|
||||||
from pan.xapi import PanXapiError
|
|
||||||
HAS_LIB = True
|
|
||||||
except ImportError:
|
|
||||||
HAS_LIB = False
|
|
||||||
|
|
||||||
_SERVICE_XPATH = "/config/devices/entry[@name='localhost.localdomain']" +\
|
|
||||||
"/vsys/entry[@name='vsys1']" +\
|
|
||||||
"/service/entry[@name='%s']"
|
|
||||||
|
|
||||||
|
|
||||||
def service_exists(xapi, service_name):
|
|
||||||
xapi.get(_SERVICE_XPATH % service_name)
|
|
||||||
e = xapi.element_root.find('.//entry')
|
|
||||||
if e is None:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def add_service(xapi, module, service_name, protocol, port, source_port):
|
|
||||||
if service_exists(xapi, service_name):
|
|
||||||
return False
|
|
||||||
|
|
||||||
exml = ['<protocol>']
|
|
||||||
exml.append('<%s>' % protocol)
|
|
||||||
exml.append('<port>%s</port>' % port)
|
|
||||||
if source_port:
|
|
||||||
exml.append('<source-port>%s</source-port>' % source_port)
|
|
||||||
exml.append('</%s>' % protocol)
|
|
||||||
exml.append('</protocol>')
|
|
||||||
|
|
||||||
exml = ''.join(exml)
|
|
||||||
|
|
||||||
xapi.set(xpath=_SERVICE_XPATH % service_name, element=exml)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
argument_spec = dict(
|
|
||||||
ip_address=dict(required=True),
|
|
||||||
password=dict(required=True, no_log=True),
|
|
||||||
username=dict(default='admin'),
|
|
||||||
service_name=dict(required=True),
|
|
||||||
protocol=dict(required=True, choices=['tcp', 'udp']),
|
|
||||||
port=dict(required=True),
|
|
||||||
source_port=dict(),
|
|
||||||
commit=dict(type='bool', default=True)
|
|
||||||
)
|
|
||||||
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False)
|
|
||||||
if not HAS_LIB:
|
|
||||||
module.fail_json(msg='pan-python is required for this module')
|
|
||||||
|
|
||||||
ip_address = module.params["ip_address"]
|
|
||||||
password = module.params["password"]
|
|
||||||
username = module.params['username']
|
|
||||||
service_name = module.params['service_name']
|
|
||||||
protocol = module.params['protocol']
|
|
||||||
port = module.params['port']
|
|
||||||
source_port = module.params['source_port']
|
|
||||||
commit = module.params['commit']
|
|
||||||
|
|
||||||
xapi = pan.xapi.PanXapi(
|
|
||||||
hostname=ip_address,
|
|
||||||
api_username=username,
|
|
||||||
api_password=password
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
changed = add_service(xapi, module,
|
|
||||||
service_name,
|
|
||||||
protocol,
|
|
||||||
port,
|
|
||||||
source_port)
|
|
||||||
if changed and commit:
|
|
||||||
xapi.commit(cmd="<commit></commit>", sync=True, interval=1)
|
|
||||||
except PanXapiError:
|
|
||||||
exc = get_exception()
|
|
||||||
module.fail_json(msg=exc.message)
|
|
||||||
|
|
||||||
module.exit_json(changed=changed, msg="okey dokey")
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
Loading…
Reference in New Issue