Esempio n. 1
0
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--panorama',
                        help='hostname or ip of panorama',
                        required=True)
    parser.add_argument('--user',
                        help='username for auth to panorama',
                        required=True)
    parser.add_argument(
        '--out_file',
        help='filename for output csv e.g. mkey_status_prod.csv',
        required=True)
    args = parser.parse_args()

    try:
        panorama = Panorama(args.panorama, args.user, getpass())
    except PanDeviceError as e:
        print(e.message)

    cmd = 'show devices connected'
    try:
        res = panorama.op(cmd, xml=True)
    except PanDeviceError as e:
        print(e.message)
        sys.exit(1)

    print('Authenticated to {}.'.format(args.panorama))
    print(
        'Generating master key status report on Panorama-connected firewalls...'
    )

    devs_connected = xmltodict.parse(
        res)['response']['result']['devices']['entry']

    master_key_props_list = []

    for dev in devs_connected:
        firewall = Firewall(serial=dev['serial'])
        panorama.add(firewall)

        cmd = 'show system masterkey-properties'
        master_key_props = xmltodict.parse(firewall.op(
            cmd, xml=True))['response']['result']
        master_key_props['hostname'] = dev['hostname']

        master_key_props_list.append(master_key_props)

    with open(args.out_file, 'w', newline='') as file_obj:
        fieldnames = master_key_props_list[0].keys()
        writer_obj = csv.DictWriter(file_obj, fieldnames=fieldnames)

        writer_obj.writeheader()
        for dev_mkey_props in master_key_props_list:
            writer_obj.writerow(dev_mkey_props)

    print('Done.')
Esempio n. 2
0
 def start(self):
     # Get/Generate the firewall API key from the credentials stored in Splunk
     if self.firewall is None:
         apikey = self.connector.apikey(self.device)
         # Perform the tagging operation on the firewall
         self.firewall = Firewall(self.device, api_key=apikey)
         self.firewall.userid.batch_start()
Esempio n. 3
0
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--panorama',
                        help='hostname or ip of panorama',
                        required=True)
    parser.add_argument('--user',
                        help='username for auth to panorama',
                        required=True)
    args = parser.parse_args()

    try:
        panorama = Panorama(args.panorama, args.user, getpass())
    except PanDeviceError as e:
        print(e.message)

    cmd = 'show devices connected'
    try:
        res = panorama.op(cmd, xml=True)
    except PanDeviceError as e:
        print(e.message)
        sys.exit(1)

    devs_connected = xmltodict.parse(
        res)['response']['result']['devices']['entry']

    ha_devices_out_of_sync = []

    for dev in devs_connected:
        firewall = Firewall(serial=dev['serial'])
        panorama.add(firewall)

        cmd = 'show high-availability state'
        ha_state = xmltodict.parse(firewall.op(cmd,
                                               xml=True))['response']['result']
        if ha_state['enabled'] == 'yes':
            if ha_state['group']['running-sync'] != 'synchronized':
                ha_devices_out_of_sync.append(dev['hostname'])

    if ha_devices_out_of_sync:
        for dev in ha_devices_out_of_sync:
            print(dev)
    else:
        print('All HA devices configuration in sync!')
Esempio n. 4
0
def main():
    global pan_device, username, password, dry_run_flag
    if len(sys.argv) == 3 and sys.argv[2] == '--dry-run':
        dry_run_flag = True
        print(
            '\n\n\n******* Dry-run flag enabled - No chages will be made *******\n\n'
        )
    elif len(sys.argv) == 2:
        pass
    else:
        time.sleep(.75)
        print(
            '\nERROR: The proper format is rename-addr-objects_multithreaded.py <addr-list.csv> [--dry-run]\n\n'
        )
        exit()
    with open(sys.argv[1], 'r') as rfile:
        print(f"\n\nCSV file found...\n\n")
        addr_list_mods = [tuple(line.rstrip().split(',')) for line in rfile]
    check_list_validity(addr_list_mods)
    pan_addr = get_pan_addr()
    username, password = get_creds()

    # Determine whether this is a Panorama or firewall
    dev_type = get_dev_type(pan_addr)
    if dev_type == 'pano':
        while True:
            pan_device = Panorama(pan_addr, username, password)
            get_pano_dg()
            addr_list_names = match_address_objects(addr_list_mods)
            push_addr_changes(addr_list_names)
            dg_choice = input(
                '\n\nWould you like to run this script against another device group? [Y/n]  '
            )
            if not dg_choice or dg_choice.lower() == 'y':
                new_list = input(
                    '\n\nEnter the name of the address list for this device group, (leave blank to use the same list): '
                )
                if not new_list:
                    new_list = sys.argv[1]
                with open(new_list, 'r') as rfile:
                    print(f"\n\nCSV file found...\n\n")
                    addr_list_mods = [
                        tuple(line.rstrip().split(',')) for line in rfile
                    ]
                check_list_validity(addr_list_mods)
            elif dg_choice.lower() == 'n':
                break
            else:
                print("\n\nChoose '1' or '2', try again...\n")
                time.sleep(.75)
    else:
        pan_device = Firewall(pan_addr, username, password)
        addr_list_names = match_address_objects(addr_list_mods)
        push_addr_changes(addr_list_names)
    print('\n\n\nHave a great day!!\n\n')
Esempio n. 5
0
    def get_pandevice_parent(self, module, timeout=0):
        """Builds the pandevice object tree, returning the parent object.

        If pandevice is not installed, then module.fail_json() will be
        invoked.

        Arguments:
            * module(AnsibleModule): the ansible module.
            * timeout(int): Number of seconds to retry opening the connection to PAN-OS.

        Returns:
            * The parent pandevice object based on the spec given to
              get_connection().
        """
        # Sanity check.
        if not HAS_PANDEVICE:
            module.fail_json(msg='Missing required library "pandevice".')

        # Verify pandevice minimum version.
        if self.min_pandevice_version is not None:
            pdv = tuple(int(x) for x in pandevice.__version__.split('.'))
            if pdv < self.min_pandevice_version:
                module.fail_json(msg=_MIN_VERSION_ERROR.format(
                    'pandevice', pandevice.__version__,
                    _vstr(self.min_pandevice_version)))

        pan_device_auth, serial_number = None, None
        if module.params['provider'] and module.params['provider'][
                'ip_address']:
            pan_device_auth = (
                module.params['provider']['ip_address'],
                module.params['provider']['username'],
                module.params['provider']['password'],
                module.params['provider']['api_key'],
                module.params['provider']['port'],
            )
            serial_number = module.params['provider']['serial_number']
        elif module.params.get('ip_address', None) is not None:
            pan_device_auth = (
                module.params['ip_address'],
                module.params['username'],
                module.params['password'],
                module.params['api_key'],
                module.params['port'],
            )
            msg = 'Classic provider params are deprecated; use "provider" instead'
            module.deprecate(msg, '2.12')
        else:
            module.fail_json(msg='Provider params are required.')

        # Create the connection object.
        if not isinstance(timeout, int):
            raise ValueError('Timeout must be an int')
        elif timeout < 0:
            raise ValueError('Timeout must greater than or equal to 0')
        end_time = time.time() + timeout
        while True:
            try:
                self.device = PanDevice.create_from_device(*pan_device_auth)
            except PanDeviceError as e:
                if timeout == 0:
                    module.fail_json(msg='Failed connection: {0}'.format(e))
                elif time.time() >= end_time:
                    module.fail_json(msg='Connection timeout: {0}'.format(e))
            else:
                break

        # Verify PAN-OS minimum version.
        if self.min_panos_version is not None:
            if self.device._version_info < self.min_panos_version:
                module.fail_json(msg=_MIN_VERSION_ERROR.format(
                    'PAN-OS', _vstr(self.device._version_info),
                    _vstr(self.min_panos_version)))

        # Optional: Firewall via Panorama connectivity specified.
        if hasattr(self.device, 'refresh_devices') and serial_number:
            fw = Firewall(serial=serial_number)
            self.device.add(fw)
            self.device = fw

        parent = self.device
        no_shared = 'Scope "shared" is not allowed'
        not_found = '{0} "{1}" is not present.'
        pano_mia_param = 'Param "{0}" is required for Panorama but not specified.'
        ts_error = 'Specify either the template or the template stack{0}.'
        if hasattr(self.device, 'refresh_devices'):
            # Panorama connection.
            templated = False

            # Error if Panorama is not supported.
            if self.panorama_error is not None:
                module.fail_json(msg=self.panorama_error)

            # Spec: template stack.
            tmpl_required = False
            added_template = False
            if self.template_stack is not None:
                name = module.params[self.template_stack]
                if name is not None:
                    templated = True
                    stacks = TemplateStack.refreshall(parent, name_only=True)
                    for ts in stacks:
                        if ts.name == name:
                            parent = ts
                            added_template = True
                            break
                    else:
                        module.fail_json(msg=not_found.format(
                            'Template stack',
                            name,
                        ))
                elif self.template is not None:
                    tmpl_required = True
                else:
                    module.fail_json(
                        msg=pano_mia_param.format(self.template_stack))

            # Spec: template.
            if self.template is not None:
                name = module.params[self.template]
                if name is not None:
                    templated = True
                    if added_template:
                        module.fail_json(msg=ts_error.format(', not both'))
                    templates = Template.refreshall(parent, name_only=True)
                    for t in templates:
                        if t.name == name:
                            parent = t
                            break
                    else:
                        module.fail_json(msg=not_found.format(
                            'Template',
                            name,
                        ))
                elif tmpl_required:
                    module.fail_json(msg=ts_error.format(''))
                elif not added_template:
                    module.fail_json(msg=pano_mia_param.format(self.template))

            # Spec: vsys_dg or device_group.
            dg_name = self.vsys_dg or self.device_group
            if dg_name is not None:
                name = module.params[dg_name]
                if name not in (None, 'shared'):
                    groups = DeviceGroup.refreshall(parent, name_only=True)
                    for dg in groups:
                        if dg.name == name:
                            parent = dg
                            break
                    else:
                        module.fail_json(msg=not_found.format(
                            'Device group',
                            name,
                        ))
                elif self.error_on_shared:
                    module.fail_json(msg=no_shared)

            # Spec: vsys importable.
            vsys_name = self.vsys_importable or self.vsys or self.vsys_shared
            if dg_name is None and templated and vsys_name is not None:
                name = module.params[vsys_name]
                if name not in (None, 'shared'):
                    vo = Vsys(name)
                    parent.add(vo)
                    parent = vo

            # Spec: rulebase.
            if self.rulebase is not None:
                if module.params[self.rulebase] in (None, 'pre-rulebase'):
                    rb = PreRulebase()
                    parent.add(rb)
                    parent = rb
                elif module.params[self.rulebase] == 'rulebase':
                    rb = Rulebase()
                    parent.add(rb)
                    parent = rb
                elif module.params[self.rulebase] == 'post-rulebase':
                    rb = PostRulebase()
                    parent.add(rb)
                    parent = rb
                else:
                    module.fail_json(msg=not_found.format(
                        'Rulebase', module.params[self.rulebase]))
        else:
            # Firewall connection.
            # Error if firewalls are not supported.
            if self.firewall_error is not None:
                module.fail_json(msg=self.firewall_error)

            # Spec: vsys or vsys_dg or vsys_importable.
            vsys_name = self.vsys_dg or self.vsys or self.vsys_importable or self.vsys_shared
            if vsys_name is not None:
                parent.vsys = module.params[vsys_name]
                if parent.vsys == 'shared' and self.error_on_shared:
                    module.fail_json(msg=no_shared)

            # Spec: rulebase.
            if self.rulebase is not None:
                rb = Rulebase()
                parent.add(rb)
                parent = rb

        # Done.
        return parent
Esempio n. 6
0
                srv_matched = True
                break

    if rule.action == "allow":
        is_allowed = True

    if all([src_matched, dst_matched, app_matched, srv_matched, is_allowed]):
        return True

    return False


if __name__ == "__main__":
    import config

    vsys = Firewall(config.host, config.api_username, config.api_password, serial=config.fw_serial, is_virtual=True, vsys=config.vsys)
    shared = Firewall(config.host, config.api_username, config.api_password, serial=config.fw_serial, is_virtual=True, vsys=config.shared_vsys)

    # Pull all address objects in vsys and shared configuration
    address = PanAddressObject.refreshall(shared)
    address.extend(PanAddressObject.refreshall(vsys))

    # Pull all policies in vsys and filter security rules out
    rulebase = Rulebase.refreshall(vsys)
    security_rules = filter(lambda x: isinstance(x, SecurityRule), rulebase[0].children)

    # Pull all service objects from vsys and shared configuration
    all_services = ServiceObject.refreshall(vsys)
    all_services.extend(ServiceObject.refreshall(shared))

    # Check if source ip address is included in any address objects
Esempio n. 7
0
def main_splunk():
    # Get arguments
    args, kwargs = splunk.Intersplunk.getKeywordsAndOptions()

    # Enable debugging by passing 'debug=yes' as an argument of
    # the command on the Splunk searchbar.

    debug = common.check_debug(kwargs)

    # kwargs contains important parameters.
    # parameters from splunk searchbar include:
    #   action
    #   device
    #   panorama
    #   serial
    #   vsys
    #   tag
    #   tag_field
    #   ip_field
    #   debug

    # Verify required args were passed to command
    log(debug, "Determining if required arguments are present")
    if 'device' not in kwargs and 'panorama' not in kwargs:
        common.exit_with_error("Missing required command argument: device or panorama", 3)
    if 'tag' not in kwargs and 'tag_field' not in kwargs:
        common.exit_with_error("Missing required command argument: tag or tag_field", 3)

    # Assign defaults to fields that aren't specified
    action = kwargs['action'] if 'action' in kwargs else "addip"
    vsys = kwargs['vsys'] if 'vsys' in kwargs else None
    ip_field = kwargs['ip_field'] if 'ip_field' in kwargs else "src_ip"
    user_field = kwargs['user_field'] if 'user_field' in kwargs else "src_user"
    # Support 'field' argument (legacy syntax)
    if 'field' in kwargs and not 'ip_field' in kwargs:
        ip_field = kwargs['field']
    tag = kwargs['tag'] if 'tag' in kwargs else None
    tag_field = kwargs['tag_field'] if 'tag_field' in kwargs else None

    # Determine if device hostname or serial was provided as argument or should be pulled from entries
    log(debug, "Determining how firewalls should be contacted based on arguments")
    use_panorama = False
    hostname = None
    serial = None
    if "device" in kwargs:
        hostname = kwargs['device']
        if vsys is None:
            vsys = "vsys1"
    elif "panorama" in kwargs:
        use_panorama = True
        hostname = kwargs['panorama']
        if "serial" in kwargs:
            serial = kwargs['serial']
            if vsys is None:
                vsys = "vsys1"
    else:
        common.exit_with_error("Missing required command argument: device or panorama", 3)
    log(debug, "Use Panorama: %s" % use_panorama)
    log(debug, "VSys: %s" % vsys)
    log(debug, "Hostname: %s" % hostname)
    if use_panorama and serial is not None:
        log(debug, "Device Serial: %s" % serial)
    else:
        log(debug, "Using serials from logs")


    # Results contains the data from the search results and settings
    # contains the sessionKey that we can use to talk to Splunk
    results, unused1, settings = splunk.Intersplunk.getOrganizedResults()
    # Get the sessionKey
    sessionKey = settings['sessionKey']

    log(debug, "Begin get API key")
    # Get the API key from the Splunk store or from the device at hostname if no apikey is stored
    apikey = common.apikey(sessionKey, hostname, debug)

    # Create the connection to the firewall or Panorama
    panorama = None
    if use_panorama:
        # For Panorama, create the Panorama object, and the firewall if only one serial
        panorama = Panorama(hostname, api_key=apikey)
        if serial is not None:
            firewall = {'firewall': Firewall(serial=serial, vsys=vsys)}
            panorama.add(firewall['firewall'])
            firewall['firewall'].userid.batch_start()
        else:
            firewall = {}
    else:
        firewall = {'firewall': Firewall(hostname, api_key=apikey, vsys=vsys)}
        firewall['firewall'].userid.batch_start()

    # Collect all the ip addresses and tags into firewall batch requests
    for result in results:

        ## Find the serial (if not a single firewall)

        if use_panorama and serial is None:
            try:
                this_serial = result['serial_number']
                this_vsys = result['vsys']
            except KeyError as e:
                result['status'] = "ERROR: Unable to determine serial number or vsys of device"
                continue
            else:
                ## Create the firewall object if using multiple serials
                if this_serial in firewall:
                    this_firewall = firewall[(this_serial, this_vsys)]
                else:
                    # Create the firewall object for this serial
                    firewall[(this_serial, this_vsys)] = Firewall(serial=this_serial, vsys=this_vsys)
                    this_firewall = firewall[(this_serial, this_vsys)]
                    panorama.add(this_firewall)
                    this_firewall.userid.batch_start()
        else:
            this_firewall = firewall['firewall']

        ## Find the tag (if a tag_field was specified)

        this_tag = []
        if tag_field is not None:
            try:
                this_tag.append(result[tag_field])
            except KeyError as e:
                result['status'] = "ERROR: Unable to determine tag from field: %s" % tag_field
                continue
        if tag is not None:
            this_tag.append(tag)

        ## Find the field

        try:
            if action in ("adduser", "removeuser"):
                this_field = result[user_field]
            else: 
                this_field = result[ip_field]
        except KeyError as e:
            result['status'] = "ERROR: Unable to determine value from field: %s" % this_field

        ## Create a request in the batch user-id update for the firewall
        ## No API call to the firewall happens until all batch requests are created.

        if action in ("add", "addip"):
            log(debug, "Registering tags on firewall %s: %s - %s" % (this_firewall, this_field, this_tag))
            this_firewall.userid.register(this_field, this_tag)
        elif action in ("remove", "removeip"):
            log(debug, "Unregistering tags on firewall %s: %s - %s" % (this_firewall, this_field, this_tag))
            this_firewall.userid.unregister(this_field, this_tag)
        elif action == "adduser":
            log(debug, "Registering tags on firewall %s: %s - %s" % (this_firewall, this_field, this_tag))
            this_firewall.userid.tag_user(this_field, this_tag)
        elif action == "removeuser":
            log(debug, "Unregistering tags on firewall %s: %s - %s" % (this_firewall, this_field, this_tag))
            this_firewall.userid.untag_user(this_field, this_tag)

        result['status'] = "Submitted successfully"

    ## Make the API calls to the User-ID API of each firewall

    try:
        for fw in list(firewall.values()):
            fw.userid.batch_end()

    except pan.xapi.PanXapiError as e:
        common.exit_with_error(str(e))

    except Exception as e:
        common.exit_with_error(str(e))

    # output results
    splunk.Intersplunk.outputResults(results)
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--panorama',
                        help='hostname or ip of panorama',
                        required=True)
    parser.add_argument(
        '--master_device',
        help='hostname or ip of firewall to retrieve group-mappings',
        required=True)
    parser.add_argument(
        '--dg',
        help=
        'device group of the pre-rulebase that contain user-group-based policies',
        required=True)
    args = parser.parse_args()

    try:
        panorama = Panorama(args.panorama, input('Panorama username: '******'Panorama password: '******'show devices connected'
    try:
        res = panorama.op(cmd, xml=True)
    except PanDeviceError as e:
        print(e.message)
        sys.exit(1)

    devs_connected = xmltodict.parse(
        res)['response']['result']['devices']['entry']

    firewall = None

    for dev in devs_connected:
        if dev['hostname'] == args.master_device or dev[
                'ip-address'] == args.master_device:
            firewall = Firewall(serial=dev['serial'])
            break

    if firewall is not None:
        try:
            panorama.add(firewall)
        except PanDeviceError as e:
            print(e.message)
    else:
        print(
            'Master device (firewall) is not managed by Panorama. Attempting direct connection to firewall...'
        )
        try:
            firewall = Firewall(args.master_device,
                                input('Firewall username: '******'Firewall password: '******'Retrieving user-group-mappings on master device: "{}"...'.format(
        args.master_device))

    cmd = 'show user group list'
    try:
        res = firewall.op(cmd, xml=True)
    except PanDeviceError as e:
        print(e.message)

    user_group_data = xmltodict.parse(res)['response']['result']
    user_group_list = re.findall(r'cn=.*?dc=com', user_group_data)

    print('Number of mapped user-groups found: {}\n'.format(
        len(user_group_list)))
    print('Currently mapped user-groups: ')
    for user_group in user_group_list:
        print('"{}"'.format(user_group))
    print('\n')

    try:
        DeviceGroup.refreshall(panorama)
        target_dg = panorama.find(args.dg, DeviceGroup)

        if target_dg is None:
            print(
                'Device group "{}" not found on Panorama device. Aborting...'.
                format(args.dg))
            sys.exit()

        prb = PreRulebase()
        target_dg.add(prb)

        dg_pre_rules = SecurityRule.refreshall(prb)
    except PanDeviceError as e:
        print(e.message)

    print('Retrieving user-based security policy from device-group: "{}"...'.
          format(args.dg))

    user_based_rules = []
    for rule in dg_pre_rules:
        if not 'any' in rule.source_user:
            user_based_rules.append(rule)

    print('Number of user-based security rules found: {}\n'.format(
        len(user_based_rules)))

    for rule in user_based_rules:
        print('Validating user-based security rule: "{}"...'.format(rule.name))
        for user in rule.source_user:
            if not user in user_group_list:
                print('Invalid user-group: "{}"'.format(user))
        print('\n')
Esempio n. 9
0
def get_firewall(device, serialnum):
    Firewall.refreshall(device, add=True)
    f = device.find(serialnum)
    return f
Esempio n. 10
0
def main():
    argument_spec = dict(ip_address=dict(required=True),
                         username=dict(default='admin'),
                         password=dict(default='admin', no_log=True),
                         api_key=dict(no_log=True),
                         serialnum=dict(required=True),
                         devicegroup=dict(),
                         templatestack=dict(),
                         state=dict(default='present'))

    module = AnsibleModule(argument_spec=argument_spec,
                           supports_check_mode=True,
                           mutually_exclusive=[['api_key', 'password']])

    if not HAS_LIB:
        module.fail_json(
            msg='pan-python and pandevice are required for this module')

    serialnum = module.params["serialnum"]
    devicegroup = module.params['devicegroup']
    templatestack = module.params['templatestack']
    state = module.params['state']

    # Get the authentication params
    auth = (
        module.params['ip_address'],
        module.params['username'],
        module.params['password'],
        module.params['api_key'],
    )

    # Open the connection to the PAN-OS device
    device = None
    try:
        device = PanDevice.create_from_device(*auth)
    except PanDeviceError:
        e = get_exception()
        module.fail_json(msg=e.message)

    # Ensure we're not connected to a firewall
    if isinstance(device, Firewall):
        module.fail_json(
            msg='This module is only supported on Panorama instances')

    # Set our change flag
    changed = False

    # Add to Panorama if not already connected
    fw = get_firewall(device, serialnum)
    if fw is None and state == 'present' and not module.check_mode:
        fw = Firewall(serial=serialnum)
        device.add(fw)
        fw.create()
        changed = True

    # Process the DeviceGroup
    if devicegroup:
        dg = get_devicegroup(device, devicegroup)
        if dg is None:
            module.fail_json(
                msg="DeviceGroup not found: {0}".format(devicegroup))
        elif dg.find(
                fw.serial, Firewall
        ) is None and state == 'present' and not module.check_mode:
            dg.add(fw)
            fw.create()
            changed = True
        elif dg.find(fw.serial,
                     Firewall) and state == 'absent' and not module.check_mode:
            dg.remove(dg.find(fw.serial))
            dg.apply()
            changed = True

    # Add to the Template Stack
    if templatestack:
        ts = get_templatestack(device, templatestack)
        if ts is None:
            module.fail_json(
                msg="TemplateStack not found: {0}".format(templatestack))
        else:
            listify(ts)
        if fw.serial not in ts.devices and state == 'present' and not module.check_mode:
            ts.devices.append(serialnum)
            ts.create()
            changed = True
        elif fw.serial in ts.devices and state == 'absent' and not module.check_mode:
            ts.devices.remove(fw.serial)
            ts.apply()
            changed = True

    # Remove from Panorama
    if (device.find(fw.serial, Firewall)) and (state == 'absent'):
        device.remove(fw)
        device.apply()
        changed = True

    module.exit_json(changed=changed)
Esempio n. 11
0
def main_splunk():
    # Get arguments
    args, kwargs = splunk.Intersplunk.getKeywordsAndOptions()

    # Enable debugging by passing 'debug=yes' as an argument of
    # the command on the Splunk searchbar.

    debug = common.check_debug(kwargs)

    # kwargs contains important parameters.
    # parameters from splunk searchbar include:
    #   action
    #   device
    #   panorama
    #   serial
    #   vsys
    #   user_field
    #   ip_field
    #   debug

    # Verify required args were passed to command
    log(debug, "Determining if required arguments are present")
    if 'device' not in kwargs and 'panorama' not in kwargs:
        common.exit_with_error(
            "Missing required command argument: device or panorama", 3)
    if 'panorama' in kwargs and 'serial' not in kwargs:
        common.exit_with_error(
            "Found 'panorama' arguments, but missing 'serial' argument", 3)

    # Assign defaults to fields that aren't specified
    action = kwargs['action'] if 'action' in kwargs else "login"
    vsys = kwargs['vsys'] if 'vsys' in kwargs else "vsys1"
    ip_field = kwargs['ip_field'] if 'ip_field' in kwargs else "src_ip"
    user_field = kwargs['user_field'] if 'user_field' in kwargs else "user"

    # Determine if device hostname or serial was provided as argument or should be pulled from entries
    log(debug,
        "Determining how firewalls should be contacted based on arguments")
    use_panorama = False
    hostname = None
    serial = None
    if "device" in kwargs:
        hostname = kwargs['device']
    elif "panorama" in kwargs:
        use_panorama = True
        hostname = kwargs['panorama']
        serial = kwargs['serial']
    else:
        common.exit_with_error(
            "Missing required command argument: device or panorama", 3)
    log(debug, "Use Panorama: %s" % use_panorama)
    log(debug, "VSys: %s" % vsys)
    log(debug, "Hostname: %s" % hostname)
    if use_panorama and serial is not None:
        log(debug, "Device Serial: %s" % serial)

    # Results contains the data from the search results and settings
    # contains the sessionKey that we can use to talk to Splunk
    results, unused1, settings = splunk.Intersplunk.getOrganizedResults()
    # Get the sessionKey
    sessionKey = settings['sessionKey']

    log(debug, "Begin get API key")
    # Get the API key from the Splunk store or from the device at hostname if no apikey is stored
    apikey = common.apikey(sessionKey, hostname, debug)

    # Create the connection to the firewall or Panorama
    panorama = None
    if use_panorama:
        # For Panorama, create the Panorama object, and the firewall object
        panorama = Panorama(hostname, api_key=apikey)
        firewall = Firewall(panorama=panorama, serial=serial, vsys=vsys)
        firewall.userid.batch_start()
    else:
        # No Panorama, so just create the firewall object
        firewall = Firewall(hostname, api_key=apikey, vsys=vsys)
        firewall.userid.batch_start()

    # Collect all the ip addresses and users into firewall batch requests
    for result in results:

        ## Find the user (if a user_field was specified)

        try:
            this_user = result[user_field]
        except KeyError as e:
            result[
                'status'] = "ERROR: Unable to determine user from field: %s" % user_field
            continue

        ## Find the IP

        try:
            this_ip = result[ip_field]
        except KeyError as e:
            result[
                'status'] = "ERROR: Unable to determine ip from field: %s" % ip_field

        ## Create a request in the batch user-id update for the firewall
        ## No API call to the firewall happens until all batch requests are created.

        if action == "login":
            log(
                debug, "Login event on firewall %s: %s - %s" %
                (firewall, this_ip, this_user))
            firewall.userid.login(this_user, this_ip)
        else:
            log(
                debug, "Logout event on firewall %s: %s - %s" %
                (firewall, this_ip, this_user))
            firewall.userid.logout(this_user, this_ip)

        result['status'] = "Submitted successfully"

    ## Make the API calls to the User-ID API of each firewall

    try:
        firewall.userid.batch_end()

    except pan.xapi.PanXapiError as e:
        common.exit_with_error(str(e))

    except Exception as e:
        common.exit_with_error(str(e))

    # output results
    splunk.Intersplunk.outputResults(results)