New module - meraki_malware (#56929)
* Initial commit for meraki_malware module - Allows for manipulation of malware configuration * Add full documentation and improve code coverage * Add diff support * Type change * Sanity check fixes * Convert org_id from str to int for consistency * Sanity fixes again * Fix argument type errors * Remove ignore items for some Meraki modules so shippable is happy
This commit is contained in:
parent
8bcf9b5734
commit
919f70c357
8 changed files with 527 additions and 11 deletions
|
@ -182,8 +182,6 @@ def main():
|
|||
# the module
|
||||
argument_spec = meraki_argument_spec()
|
||||
argument_spec.update(state=dict(type='str', choices=['absent', 'query', 'present'], default='query'),
|
||||
org_name=dict(type='str', aliases=['organization']),
|
||||
org_id=dict(type='int'),
|
||||
config_template=dict(type='str', aliases=['name']),
|
||||
net_name=dict(type='str'),
|
||||
net_id=dict(type='str'),
|
||||
|
|
276
lib/ansible/modules/network/meraki/meraki_malware.py
Normal file
276
lib/ansible/modules/network/meraki/meraki_malware.py
Normal file
|
@ -0,0 +1,276 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2019, Kevin Breit (@kbreit) <kevin.breit@kevinbreit.net>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'
|
||||
}
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: meraki_malware
|
||||
short_description: Manage Malware Protection in the Meraki cloud
|
||||
version_added: "2.9"
|
||||
description:
|
||||
- Fully configure malware protection in a Meraki environment.
|
||||
notes:
|
||||
- Some of the options are likely only used for developers within Meraki.
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- Specifies whether object should be queried, created/modified, or removed.
|
||||
choices: [absent, present, query]
|
||||
default: query
|
||||
type: str
|
||||
net_name:
|
||||
description:
|
||||
- Name of network which configuration is applied to.
|
||||
aliases: [network]
|
||||
type: str
|
||||
net_id:
|
||||
description:
|
||||
- ID of network which configuration is applied to.
|
||||
type: str
|
||||
allowed_urls:
|
||||
description:
|
||||
- List of URLs to whitelist.
|
||||
suboptions:
|
||||
url:
|
||||
description:
|
||||
- URL string to allow.
|
||||
type: str
|
||||
comment:
|
||||
description:
|
||||
- Human readable information about URL.
|
||||
type: str
|
||||
allowed_files:
|
||||
description:
|
||||
- List of files to whitelist.
|
||||
suboptions:
|
||||
sha256:
|
||||
description:
|
||||
- 256-bit hash of file.
|
||||
type: str
|
||||
aliases: [ hash ]
|
||||
comment:
|
||||
description:
|
||||
- Human readable information about file.
|
||||
type: str
|
||||
mode:
|
||||
description:
|
||||
- Enabled or disabled state of malware protection.
|
||||
choices: [disabled, enabled]
|
||||
|
||||
|
||||
author:
|
||||
- Kevin Breit (@kbreit)
|
||||
extends_documentation_fragment: meraki
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Enable malware protection
|
||||
meraki_malware:
|
||||
auth_key: abc123
|
||||
state: present
|
||||
org_name: YourOrg
|
||||
net_name: YourNet
|
||||
mode: enabled
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Set whitelisted url
|
||||
meraki_malware:
|
||||
auth_key: abc123
|
||||
state: present
|
||||
org_name: YourOrg
|
||||
net_name: YourNet
|
||||
mode: enabled
|
||||
allowed_urls:
|
||||
- url: www.google.com
|
||||
comment: Google
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Set whitelisted file
|
||||
meraki_malware:
|
||||
auth_key: abc123
|
||||
state: present
|
||||
org_name: YourOrg
|
||||
net_name: YourNet
|
||||
mode: enabled
|
||||
allowed_files:
|
||||
- sha256: e82c5f7d75004727e1f3b94426b9a11c8bc4c312a9170ac9a73abace40aef503
|
||||
comment: random zip
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Get malware settings
|
||||
meraki_malware:
|
||||
auth_key: abc123
|
||||
state: query
|
||||
org_name: YourNet
|
||||
net_name: YourOrg
|
||||
delegate_to: localhost
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
data:
|
||||
description: List of administrators.
|
||||
returned: success
|
||||
type: complex
|
||||
contains:
|
||||
mode:
|
||||
description: Mode to enable or disable malware scanning.
|
||||
returned: success
|
||||
type: str
|
||||
sample: enabled
|
||||
allowed_files:
|
||||
description: List of files which are whitelisted.
|
||||
returned: success
|
||||
type: complex
|
||||
contains:
|
||||
sha256:
|
||||
description: sha256 hash of whitelisted file.
|
||||
returned: success
|
||||
type: str
|
||||
sample: e82c5f7d75004727e1f3b94426b9a11c8bc4c312a9170ac9a73abace40aef503
|
||||
comment:
|
||||
description: Comment about the whitelisted entity
|
||||
returned: success
|
||||
type: str
|
||||
sample: TPS report
|
||||
allowed_urls:
|
||||
description: List of URLs which are whitelisted.
|
||||
returned: success
|
||||
type: complex
|
||||
contains:
|
||||
url:
|
||||
description: URL of whitelisted site.
|
||||
returned: success
|
||||
type: str
|
||||
sample: site.com
|
||||
comment:
|
||||
description: Comment about the whitelisted entity
|
||||
returned: success
|
||||
type: str
|
||||
sample: Corporate HQ
|
||||
'''
|
||||
|
||||
import os
|
||||
from ansible.module_utils.basic import AnsibleModule, json, env_fallback
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.common.dict_transformations import recursive_diff
|
||||
from ansible.module_utils.network.meraki.meraki import MerakiModule, meraki_argument_spec
|
||||
|
||||
|
||||
def main():
|
||||
# define the available arguments/parameters that a user can pass to
|
||||
# the module
|
||||
|
||||
urls_arg_spec = dict(url=dict(type='str'),
|
||||
comment=dict(type='str'),
|
||||
)
|
||||
|
||||
files_arg_spec = dict(sha256=dict(type='str', aliases=['hash']),
|
||||
comment=dict(type='str'),
|
||||
)
|
||||
|
||||
argument_spec = meraki_argument_spec()
|
||||
argument_spec.update(state=dict(type='str', choices=['absent', 'present', 'query'], default='query'),
|
||||
net_name=dict(type='str', aliases=['network']),
|
||||
net_id=dict(type='str'),
|
||||
mode=dict(type='str', choices=['enabled', 'disabled']),
|
||||
allowed_urls=dict(type='list', default=None, elements='dict', options=urls_arg_spec),
|
||||
allowed_files=dict(type='list', default=None, elements='dict', options=files_arg_spec),
|
||||
)
|
||||
|
||||
# seed the result dict in the object
|
||||
# we primarily care about changed and state
|
||||
# change is if this module effectively modified the target
|
||||
# state will include any data that you want your module to pass back
|
||||
# for consumption, for example, in a subsequent task
|
||||
result = dict(
|
||||
changed=False,
|
||||
)
|
||||
# the AnsibleModule object will be our abstraction working with Ansible
|
||||
# this includes instantiation, a couple of common attr would be the
|
||||
# args/params passed to the execution, as well as if the module
|
||||
# supports check mode
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
meraki = MerakiModule(module, function='malware')
|
||||
|
||||
meraki.params['follow_redirects'] = 'all'
|
||||
|
||||
query_url = {'malware': '/networks/{net_id}/security/malwareSettings'}
|
||||
update_url = {'malware': '/networks/{net_id}/security/malwareSettings'}
|
||||
|
||||
meraki.url_catalog['get_one'].update(query_url)
|
||||
meraki.url_catalog['update'] = update_url
|
||||
|
||||
org_id = meraki.params['org_id']
|
||||
if org_id is None:
|
||||
org_id = meraki.get_org_id(meraki.params['org_name'])
|
||||
net_id = meraki.params['net_id']
|
||||
if net_id is None:
|
||||
nets = meraki.get_nets(org_id=org_id)
|
||||
net_id = meraki.get_net_id(net_name=meraki.params['net_name'], data=nets)
|
||||
|
||||
# Check for argument completeness
|
||||
if meraki.params['state'] == 'present':
|
||||
if meraki.params['allowed_files'] is not None or meraki.params['allowed_urls'] is not None:
|
||||
if meraki.params['mode'] is None:
|
||||
meraki.fail_json(msg="mode must be set when allowed_files or allowed_urls is set.")
|
||||
|
||||
# Assemble payload
|
||||
if meraki.params['state'] == 'present':
|
||||
payload = dict()
|
||||
if meraki.params['mode'] is not None:
|
||||
payload['mode'] = meraki.params['mode']
|
||||
if meraki.params['allowed_urls'] is not None:
|
||||
payload['allowedUrls'] = meraki.params['allowed_urls']
|
||||
if meraki.params['allowed_files'] is not None:
|
||||
payload['allowedFiles'] = meraki.params['allowed_files']
|
||||
|
||||
if meraki.params['state'] == 'query':
|
||||
path = meraki.construct_path('get_one', net_id=net_id)
|
||||
data = meraki.request(path, method='GET')
|
||||
if meraki.status == 200:
|
||||
meraki.result['data'] = data
|
||||
elif meraki.params['state'] == 'present':
|
||||
path = meraki.construct_path('get_one', net_id=net_id)
|
||||
original = meraki.request(path, method='GET')
|
||||
if meraki.is_update_required(original, payload):
|
||||
if meraki.module.check_mode is True:
|
||||
diff = recursive_diff(original, payload)
|
||||
original.update(payload)
|
||||
meraki.result['diff'] = {'before': diff[0],
|
||||
'after': diff[1],
|
||||
}
|
||||
meraki.result['data'] = original
|
||||
meraki.result['changed'] = True
|
||||
meraki.exit_json(**meraki.result)
|
||||
path = meraki.construct_path('update', net_id=net_id)
|
||||
data = meraki.request(path, method='PUT', payload=json.dumps(payload))
|
||||
if meraki.status == 200:
|
||||
diff = recursive_diff(original, payload)
|
||||
meraki.result['diff'] = {'before': diff[0],
|
||||
'after': diff[1],
|
||||
}
|
||||
meraki.result['data'] = data
|
||||
meraki.result['changed'] = True
|
||||
else:
|
||||
meraki.result['data'] = original
|
||||
|
||||
# in the event of a successful module execution, you will want to
|
||||
# simple AnsibleModule.exit_json(), passing the key/value results
|
||||
meraki.exit_json(**meraki.result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -38,6 +38,7 @@ options:
|
|||
description:
|
||||
- ID of organization.
|
||||
aliases: [ id ]
|
||||
type: str
|
||||
author:
|
||||
- Kevin Breit (@kbreit)
|
||||
extends_documentation_fragment: meraki
|
||||
|
@ -124,7 +125,7 @@ def main():
|
|||
argument_spec.update(clone=dict(type='str'),
|
||||
state=dict(type='str', choices=['present', 'query'], default='present'),
|
||||
org_name=dict(type='str', aliases=['name', 'organization']),
|
||||
org_id=dict(type='int', aliases=['id']),
|
||||
org_id=dict(type='str', aliases=['id']),
|
||||
)
|
||||
|
||||
# seed the result dict in the object
|
||||
|
|
|
@ -219,8 +219,6 @@ def main():
|
|||
# the module
|
||||
argument_spec = meraki_argument_spec()
|
||||
argument_spec.update(state=dict(type='str', choices=['present', 'query'], default='present'),
|
||||
org_name=dict(type='str', aliases=['organization']),
|
||||
org_id=dict(type='int'),
|
||||
v2c_enabled=dict(type='bool'),
|
||||
v3_enabled=dict(type='bool'),
|
||||
v3_auth_mode=dict(type='str', choices=['SHA', 'MD5']),
|
||||
|
|
|
@ -56,5 +56,5 @@ options:
|
|||
org_id:
|
||||
description:
|
||||
- ID of organization.
|
||||
type: int
|
||||
type: str
|
||||
'''
|
||||
|
|
1
test/integration/targets/meraki_malware/aliases
Normal file
1
test/integration/targets/meraki_malware/aliases
Normal file
|
@ -0,0 +1 @@
|
|||
unsupported
|
247
test/integration/targets/meraki_malware/tasks/main.yml
Normal file
247
test/integration/targets/meraki_malware/tasks/main.yml
Normal file
|
@ -0,0 +1,247 @@
|
|||
# Test code for the Meraki VLAN module
|
||||
# Copyright: (c) 2018, Kevin Breit (@kbreit)
|
||||
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
---
|
||||
- block:
|
||||
- name: Test an API key is provided
|
||||
fail:
|
||||
msg: Please define an API key
|
||||
when: auth_key is not defined
|
||||
|
||||
- name: Create test network
|
||||
meraki_network:
|
||||
auth_key: '{{auth_key}}'
|
||||
state: present
|
||||
org_name: '{{test_org_name}}'
|
||||
net_name: '{{test_net_name}} - Malware'
|
||||
type: appliance
|
||||
delegate_to: localhost
|
||||
register: net
|
||||
|
||||
- set_fact:
|
||||
net_id: '{{net.data.id}}'
|
||||
|
||||
- name: Enable malware protection with check mode
|
||||
meraki_malware:
|
||||
auth_key: '{{auth_key}}'
|
||||
state: present
|
||||
org_name: '{{test_org_name}}'
|
||||
net_name: '{{test_net_name}} - Malware'
|
||||
mode: enabled
|
||||
delegate_to: localhost
|
||||
check_mode: yes
|
||||
register: get_malware_check
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- get_malware_check is changed
|
||||
- get_malware_check.data is defined
|
||||
|
||||
- name: Enable malware protection
|
||||
meraki_malware:
|
||||
auth_key: '{{auth_key}}'
|
||||
state: present
|
||||
org_name: '{{test_org_name}}'
|
||||
net_name: '{{test_net_name}} - Malware'
|
||||
mode: enabled
|
||||
delegate_to: localhost
|
||||
register: get_malware
|
||||
|
||||
- debug:
|
||||
var: get_malware
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- get_malware is changed
|
||||
- get_malware.data.mode is defined
|
||||
|
||||
- name: Enable malware protection with idempotency
|
||||
meraki_malware:
|
||||
auth_key: '{{auth_key}}'
|
||||
state: present
|
||||
org_name: '{{test_org_name}}'
|
||||
net_name: '{{test_net_name}} - Malware'
|
||||
mode: enabled
|
||||
delegate_to: localhost
|
||||
register: get_malware_idempotent
|
||||
|
||||
- debug:
|
||||
var: get_malware_idempotent
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- get_malware_idempotent is not changed
|
||||
- get_malware_idempotent.data is defined
|
||||
|
||||
- name: Test error when mode is not set
|
||||
meraki_malware:
|
||||
auth_key: '{{auth_key}}'
|
||||
state: present
|
||||
org_name: '{{test_org_name}}'
|
||||
net_name: '{{test_net_name}} - Malware'
|
||||
allowed_files:
|
||||
- sha256: e82c5f7d75004727e1f3b94426b9a11c8bc4c312a9170ac9a73abace40aef503
|
||||
comment: random zip
|
||||
delegate_to: localhost
|
||||
register: test_mode_err
|
||||
ignore_errors: yes
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- test_mode_err.msg == "mode must be set when allowed_files or allowed_urls is set."
|
||||
|
||||
- name: Set whitelisted file with check mode
|
||||
meraki_malware:
|
||||
auth_key: '{{auth_key}}'
|
||||
state: present
|
||||
org_name: '{{test_org_name}}'
|
||||
net_name: '{{test_net_name}} - Malware'
|
||||
mode: enabled
|
||||
allowed_files:
|
||||
- sha256: e82c5f7d75004727e1f3b94426b9a11c8bc4c312a9170ac9a73abace40aef503
|
||||
comment: random zip
|
||||
delegate_to: localhost
|
||||
check_mode: yes
|
||||
register: set_file_check
|
||||
|
||||
- debug:
|
||||
var:
|
||||
set_file_check
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- set_file_check is changed
|
||||
- set_file_check.data is defined
|
||||
|
||||
- name: Set whitelisted file
|
||||
meraki_malware:
|
||||
auth_key: '{{auth_key}}'
|
||||
state: present
|
||||
org_name: '{{test_org_name}}'
|
||||
net_id: '{{net_id}}'
|
||||
mode: enabled
|
||||
allowed_files:
|
||||
- sha256: e82c5f7d75004727e1f3b94426b9a11c8bc4c312a9170ac9a73abace40aef503
|
||||
comment: random zip
|
||||
delegate_to: localhost
|
||||
register: set_file
|
||||
|
||||
- debug:
|
||||
var: set_file
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- set_file is changed
|
||||
- set_file.data.mode is defined
|
||||
|
||||
- name: Set whitelisted file with idempotency
|
||||
meraki_malware:
|
||||
auth_key: '{{auth_key}}'
|
||||
state: present
|
||||
org_name: '{{test_org_name}}'
|
||||
net_name: '{{test_net_name}} - Malware'
|
||||
mode: enabled
|
||||
allowed_files:
|
||||
- sha256: e82c5f7d75004727e1f3b94426b9a11c8bc4c312a9170ac9a73abace40aef503
|
||||
comment: random zip
|
||||
delegate_to: localhost
|
||||
register: set_file_idempotent
|
||||
|
||||
- debug:
|
||||
var: set_file_idempotent
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- set_file_idempotent is not changed
|
||||
- set_file_idempotent.data is defined
|
||||
|
||||
- name: Set whitelisted url with check mode
|
||||
meraki_malware:
|
||||
auth_key: '{{auth_key}}'
|
||||
state: present
|
||||
org_name: '{{test_org_name}}'
|
||||
net_name: '{{test_net_name}} - Malware'
|
||||
mode: enabled
|
||||
allowed_urls:
|
||||
- url: www.google.com
|
||||
comment: Google
|
||||
delegate_to: localhost
|
||||
check_mode: yes
|
||||
register: set_url_check
|
||||
|
||||
- debug:
|
||||
var:
|
||||
set_url_check
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- set_url_check is changed
|
||||
- set_url_check.data is defined
|
||||
|
||||
- name: Set whitelisted url
|
||||
meraki_malware:
|
||||
auth_key: '{{auth_key}}'
|
||||
state: present
|
||||
org_name: '{{test_org_name}}'
|
||||
net_name: '{{test_net_name}} - Malware'
|
||||
mode: enabled
|
||||
allowed_urls:
|
||||
- url: www.google.com
|
||||
comment: Google
|
||||
delegate_to: localhost
|
||||
register: set_url
|
||||
|
||||
- debug:
|
||||
var: set_url
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- set_url is changed
|
||||
- set_url.data.mode is defined
|
||||
|
||||
- name: Set whitelisted url with idempotency
|
||||
meraki_malware:
|
||||
auth_key: '{{auth_key}}'
|
||||
state: present
|
||||
org_name: '{{test_org_name}}'
|
||||
net_name: '{{test_net_name}} - Malware'
|
||||
mode: enabled
|
||||
allowed_urls:
|
||||
- url: www.google.com
|
||||
comment: Google
|
||||
delegate_to: localhost
|
||||
register: set_url_idempotent
|
||||
|
||||
- debug:
|
||||
var: set_url_idempotent
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- set_url_idempotent is not changed
|
||||
- set_url_idempotent.data is defined
|
||||
|
||||
- name: Get malware settings
|
||||
meraki_malware:
|
||||
auth_key: '{{auth_key}}'
|
||||
state: query
|
||||
org_name: '{{test_org_name}}'
|
||||
net_name: '{{test_net_name}} - Malware'
|
||||
delegate_to: localhost
|
||||
register: get_malware
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- get_malware.data is defined
|
||||
|
||||
#############################################################################
|
||||
# Tear down starts here
|
||||
#############################################################################
|
||||
always:
|
||||
- name: Delete test network
|
||||
meraki_network:
|
||||
auth_key: '{{auth_key}}'
|
||||
state: absent
|
||||
org_name: '{{test_org_name}}'
|
||||
net_name: '{{test_net_name}} - Malware'
|
||||
delegate_to: localhost
|
|
@ -536,13 +536,8 @@ lib/ansible/modules/network/junos/junos_logging.py E322
|
|||
lib/ansible/modules/network/junos/junos_rpc.py E326
|
||||
lib/ansible/modules/network/junos/junos_static_route.py E322
|
||||
lib/ansible/modules/network/junos/junos_vrf.py E324
|
||||
lib/ansible/modules/network/meraki/meraki_device.py E325
|
||||
lib/ansible/modules/network/meraki/meraki_mr_l3_firewall.py E325
|
||||
lib/ansible/modules/network/meraki/meraki_mx_l3_firewall.py E325
|
||||
lib/ansible/modules/network/meraki/meraki_network.py E325
|
||||
lib/ansible/modules/network/meraki/meraki_ssid.py E325
|
||||
lib/ansible/modules/network/meraki/meraki_switchport.py E325
|
||||
lib/ansible/modules/network/meraki/meraki_vlan.py E325
|
||||
lib/ansible/modules/network/netact/netact_cm_command.py E326
|
||||
lib/ansible/modules/network/netconf/netconf_config.py E326
|
||||
lib/ansible/modules/network/netscaler/netscaler_cs_action.py E323
|
||||
|
|
Loading…
Reference in a new issue