Giter VIP home page Giter VIP logo

ansible-meraki's Introduction

cisco.meraki Ansible Collection

*Announcement: Cisco has announced an official Ansible collection for Meraki. On May 1, 2024, this repository will be archived and no longer maintained.

Ansible collection for managing and automating Cisco Meraki network environments.

See the CHANGELOG.md file for details of updates in each release.

Requirements

  • Ansible v2.10 or newer is required for collection support

What is Cisco Meraki?

Cisco Meraki is an easy-to-use, cloud-based, network infrastructure platform for enterprise environments. While most network hardware uses command-line interfaces (CLIs) for configuration, Meraki uses an easy-to-use Dashboard hosted in the Meraki cloud. No on-premises management hardware or software is required - only the network infrastructure to run your business.

MS Switches

Meraki MS switches come in multiple flavors and form factors. Meraki switches support 10/100/1000/10000 ports, as well as Cisco's mGig technology for 2.5/5/10Gbps copper connectivity. 8, 24, and 48 port flavors are available with PoE (802.3af/802.3at/UPoE) available on many models.

MX Firewalls

Meraki's MX firewalls support full layer 3-7 deep packet inspection. MX firewalls are compatible with a variety of VPN technologies including IPSec, SSL VPN, and Meraki's easy-to-use AutoVPN.

MR Wireless Access Points

MR access points are enterprise-class, high-performance access points for the enterprise. MR access points have MIMO technology and integrated beamforming built-in for high performance applications. BLE allows for advanced location applications to be developed with no on-premises analytics platforms.

Using the Meraki modules

Meraki modules provide a user-friendly interface to manage your Meraki environment using Ansible. For example, details about SNMP settings for a particular organization can be discovered using the module meraki_snmp <meraki_snmp_module>.

    - name: Query SNMP settings
      meraki_snmp:
        api_key: abc123
        org_name: AcmeCorp
        state: query
      delegate_to: localhost

Information about a particular object can be queried. For example, the meraki_admin <meraki_admin_module> module supports

    - name: Gather information about Jane Doe
      meraki_admin:
        api_key: abc123
        org_name: AcmeCorp
        state: query
        email: [email protected]
      delegate_to: localhost

Common Parameters

All Ansible Meraki modules support the following parameters which affect communication with the Meraki Dashboard API. Most of these should only be used by Meraki developers and not the general public.

  • host

    • Hostname or IP of Meraki Dashboard.
  • use_https

    • Specifies whether communication should be over HTTPS. (Defaults to yes)
  • use_proxy

    • Whether to use a proxy for any communication.
  • validate_certs

    • Determine whether certificates should be validated or trusted. (Defaults to yes)

These are the common parameters which are used for most every module.

  • org_name

    • Name of organization to perform actions in.
  • org_id

    • ID of organization to perform actions in.
  • net_name

    • Name of network to perform actions in.
  • net_id

    • ID of network to perform actions in.
  • state

    • General specification of what action to take. query does lookups. present creates or edits. absent deletes.

Note: Use the org_id and net_id parameters when possible. org_name and net_name require additional behind-the-scenes API calls to learn the ID values. org_id and net_id will perform faster.

Meraki Authentication

All API access with the Meraki Dashboard requires an API key. An API key can be generated from the organization's settings page. Each play in a playbook requires the api_key parameter to be specified.

The "Vault" feature of Ansible allows you to keep sensitive data such as passwords or keys in encrypted files, rather than as plain text in your playbooks or roles. These vault files can then be distributed or placed in source control. See Using Vaults in playbooks for more information.

Meraki's API returns a 404 error if the API key is not correct. It does not provide any specific error saying the key is incorrect. If you receive a 404 error, check the API key first.

Returned Data Structures

Meraki and its related Ansible modules return most information in the form of a list. For example, this is returned information by meraki_admin querying administrators. It returns a list even though there's only one.

    [
        {
            "orgAccess": "full", 
            "name": "John Doe",
            "tags": [],
            "networks": [],
            "email": "[email protected]",
            "id": "12345677890"
        }
    ]

Handling Returned Data

Since Meraki's response data uses lists instead of properly keyed dictionaries for responses, certain strategies should be used when querying data for particular information. For many situations, use the selectattr() Jinja2 function.

Merging Existing and New Data

Ansible's Meraki modules do not allow for manipulating data. For example, you may need to insert a rule in the middle of a firewall ruleset. Ansible and the Meraki modules lack a way to directly merge to manipulate data. However, a playlist can use a few tasks to split the list where you need to insert a rule and then merge them together again with the new rule added. The steps involved are as follows:

  1. Create blank "front" and "back" lists.
        vars:
          - front_rules: []
          - back_rules: []
  1. Get existing firewall rules from Meraki and create a new variable.
        - name: Get firewall rules
          meraki_mx_l3_firewall:
            auth_key: abc123
            org_name: YourOrg
            net_name: YourNet
            state: query
          delegate_to: localhost
          register: rules
        - set_fact:
            original_ruleset: '{{rules.data}}'
  1. Write the new rule. The new rule needs to be in a list so it can be merged with other lists in an upcoming step. The blank - puts the rule in a list so it can be merged.
        - set_fact:
            new_rule:
              - 
                - comment: Block traffic to server
                  src_cidr: 192.0.1.0/24
                  src_port: any
                  dst_cidr: 192.0.1.2/32
                  dst_port: any
                  protocol: any
                  policy: deny
  1. Split the rules into two lists. This assumes the existing ruleset is 2 rules long.
        - set_fact:
            front_rules: '{{front_rules + [ original_ruleset[:1] ]}}'
        - set_fact:
            back_rules: '{{back_rules + [ original_ruleset[1:] ]}}'
  1. Merge rules with the new rule in the middle.
        - set_fact:
            new_ruleset: '{{front_rules + new_rule + back_rules}}'
  1. Upload new ruleset to Meraki.
        - name: Set two firewall rules
          meraki_mx_l3_firewall:
            auth_key: abc123
            org_name: YourOrg
            net_name: YourNet
            state: present
            rules: '{{ new_ruleset }}'
          delegate_to: localhost

Error Handling

Ansible's Meraki modules will often fail if improper or incompatible parameters are specified. However, there will likely be scenarios where the module accepts the information but the Meraki API rejects the data. If this happens, the error will be returned in the body field for HTTP status of 400 return code.

Meraki's API returns a 404 error if the API key is not correct. It does not provide any specific error saying the key is incorrect. If you receive a 404 error, check the API key first. 404 errors can also occur if improper object IDs (ex. org_id) are specified.

ansible-meraki's People

Contributors

alancoding avatar felixfontein avatar hannut avatar jeffkala avatar joshuajcoronado avatar jsachnet avatar kbreit avatar larsrei avatar mksbcisco avatar mystery-rabbit avatar oliverl-21 avatar pokepoke81 avatar supertylerc avatar y0rune avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ansible-meraki's Issues

ansible-meraki module for licenses

I would love to see an ansible-meraki module for Meraki licenses. Normally we need to call support to move licenses between orgs, but it looks like they have added this to the endpoint so it would be great to be able to manage licenses with ansible. This also helps automate the claiming of devices too as we could add new licenses and deploy them at the same time. I'm pretty excited about this one ;-)

Ansible Galaxy linting errors

plugins/modules/meraki_mx_l3_firewall.py:224:1: E302 expected 2 blank lines, found 1 
plugins/modules/meraki_static_route.py:243:1: E302 expected 2 blank lines, found 1 
plugins/modules/meraki_static_route.py:249:1: E302 expected 2 blank lines, found 1 
plugins/modules/meraki_management_interface.py:254:1: F401 'os' imported but unused 
plugins/modules/meraki_management_interface.py:255:1: F401 'ansible.module_utils.basic.env_fallback' imported but unused 
plugins/modules/meraki_management_interface.py:256:1: F401 'ansible.module_utils.urls.fetch_url' imported but unused 
plugins/modules/meraki_management_interface.py:257:1: F401 'ansible.module_utils._text.to_native' imported but unused 
plugins/modules/meraki_management_interface.py:289:5: F841 local variable 'result' is assigned to but never used
plugins/modules/meraki_mx_uplink.py:198:1: F401 'os' imported but unused 
plugins/modules/meraki_mx_uplink.py:199:1: F401 'ansible.module_utils.basic.env_fallback' imported but unused 
plugins/modules/meraki_mx_uplink.py:200:1: F401 'ansible.module_utils._text.to_native' imported but unused 
plugins/modules/meraki_mx_uplink.py:201:1: F401 'ansible.module_utils.common.dict_transformations.snake_dict_to_camel_dict' imported but unused 
plugins/modules/meraki_mx_uplink.py:253:5: F841 local variable 'result' is assigned to but never used 

meraki_admin - Parameters need to be rewritten for sanity

Two of the parameters in meraki_admin come back as invalid due to sanity checking and seeing they're JSON format. This should be rewritten for regular parameter patterns.

ERROR: Found 2 validate-modules issue(s) which need to be resolved:
ERROR: plugins/modules/meraki_admin.py:0:0: parameter-type-not-in-doc: Argument 'networks' in argument_spec defines type as 'json' but documentation doesn't define type
ERROR: plugins/modules/meraki_admin.py:0:0: parameter-type-not-in-doc: Argument 'tags' in argument_spec defines type as 'json' but documentation doesn't define type

meraki_static_route - Integration test calls undefined variable

TASK [meraki_static_route : assert] ********************************************
fatal: [localhost]: FAILED! => {"msg": "The conditional check 'fixed_ip.data.fixedIpAssignments | length == 1' failed. The error was: error while evaluating conditional (fixed_ip.data.fixedIpAssignments | length == 1): 'dict object' has no attribute 'fixedIpAssignments'"}

I would like the ability to manage the MS device management interfaces

Currently the following only works for MX devices - https://n314.meraki.com/Wireless-Templat/n/6TZSfbs/manage/support/api_docs/v1#management-interface-settings

What's super annoying is that you can query the management interface on a switch, but you can't set it.

    - name: set vlan on management information
      meraki_management_interface:
        auth_key: "{{ auth_key }}"
        org_name: "{{  org_name  }}"
        net_name: "{{  network_name  }}"
        state: "present"
        serial: "{{  ms_serial  }}"
        wan1:
          wan_enabled: "enabled"
          using_static_ip: false
          vlan: "30"
      delegate_to: localhost
      register: man_info
      tags:
        - "query_man"
        - "setup_switch"        

    - name: query management information
      meraki_management_interface:
        auth_key: "{{ auth_key }}"
        org_name: "{{  org_name  }}"
        net_name: "{{  network_name  }}"
        state: "query"
        serial: "{{  ms_serial  }}"
      delegate_to: localhost
      register: man_info
      tags:
        - "query_man"
        - "setup_switch"

    - debug: msg="{{  man_info  }}"
      tags:
        - "query_man"

Output

TASK [set vlan on management information] ***************************************************************************************************************
fatal: [localhost -> localhost]: FAILED! => changed=false 
  msg: 'Unsupported parameters for (meraki_management_interface) module: man1 Supported parameters include: auth_key, host, internal_error_retry_time, net_id, net_name, org_id, org_name, output_format, output_level, rate_limit_retry_time, serial, state, timeout, use_https, use_proxy, validate_certs, wan1, wan2'

PLAY RECAP **********************************************************************************************************************************************
localhost                  : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

(ansible-silverpeak) flynn@NCL-WL-16612:~/ansible-silverpeak/ansible$ ./configure_meraki_switch.sh -e site_code="f-pascu" --tags "query_man"

PLAY [all] **********************************************************************************************************************************************

TASK [set vlan on management information] ***************************************************************************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: TypeError: 'NoneType' object is not subscriptable
fatal: [localhost -> localhost]: FAILED! => changed=false 
  module_stderr: |-
    Traceback (most recent call last):
      File "/home/flynn/.ansible/tmp/ansible-tmp-1604013886.165115-173013334994219/AnsiballZ_meraki_management_interface.py", line 102, in <module>
        _ansiballz_main()
      File "/home/flynn/.ansible/tmp/ansible-tmp-1604013886.165115-173013334994219/AnsiballZ_meraki_management_interface.py", line 94, in _ansiballz_main
        invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
      File "/home/flynn/.ansible/tmp/ansible-tmp-1604013886.165115-173013334994219/AnsiballZ_meraki_management_interface.py", line 40, in invoke_module
        runpy.run_module(mod_name='ansible_collections.cisco.meraki.plugins.modules.meraki_management_interface', init_globals=None, run_name='__main__', alter_sys=False)
      File "/home/flynn/.pyenv/versions/3.7.4/lib/python3.7/runpy.py", line 208, in run_module
        return _run_code(code, {}, init_globals, run_name, mod_spec)
      File "/home/flynn/.pyenv/versions/3.7.4/lib/python3.7/runpy.py", line 85, in _run_code
        exec(code, run_globals)
      File "/tmp/ansible_meraki_management_interface_payload_3u_uwcj4/ansible_meraki_management_interface_payload.zip/ansible_collections/cisco/meraki/plugins/modules/meraki_management_interface.py", line 369, in <module>
      File "/tmp/ansible_meraki_management_interface_payload_3u_uwcj4/ansible_meraki_management_interface_payload.zip/ansible_collections/cisco/meraki/plugins/modules/meraki_management_interface.py", line 356, in main
    TypeError: 'NoneType' object is not subscriptable
  module_stdout: ''
  msg: |-
    MODULE FAILURE
    See stdout/stderr for the exact error
  rc: 1

Query output

TASK [query management information] *********************************************************************************************************************
ok: [localhost -> localhost]

TASK [debug] ********************************************************************************************************************************************
ok: [localhost] => 
  msg:
    changed: false
    data:
      wan1:
        using_static_ip: false
        vlan: 30
    failed: false
    response: OK (unknown bytes)
    status: 200

I feel like it would be so easy for them to enable it for switches too.

setting hostname via ansible not working (meraki_device)

When device is already in the network and I'm running this code to add a device to the network it's WORKING fine;

  meraki_device:
    auth_key: "{{ MERAKI_API_KEY }}"
    org_id: "{{ meraki_org_id }}"
    state: present
    net_name: "{{ net_name }}"
    serial: "{{ serial }}"

but when adding hostname it FAILS!

  meraki_device:
    auth_key: "{{ MERAKI_API_KEY }}"
    org_id: "{{ meraki_org_id }}"
    state: present
    net_name: "{{ net_name }}"
    serial: "{{ serial }}"
    hostname: "{{ hostname }}"

but when I add tags instead of hostname it WORKS again, but tags are not added; (changed = false)

  meraki_device:
    auth_key: "{{ MERAKI_API_KEY }}"
    org_id: "{{ meraki_org_id }}"
    state: present
    net_name: "{{ net_name }}"
    serial: "{{ serial }}"
    tags: "Ansible-managed"

updating device with code with hostname in it; FAILS

  meraki_device:
    auth_key: "{{ MERAKI_API_KEY }}"
    org_id: "{{ meraki_org_id }}"
    state: present
    net_name: "{{ net_name }}"
    serial: "{{ serial }}"
    hostname: "{{ hostname }}"

but with code, it works (no fail, but ansible doesn't change anything..

  meraki_device:
    auth_key: "{{ MERAKI_API_KEY }}"
    org_id: "{{ meraki_org_id }}"
    state: present
    net_id: "{{ net_id }}"
    serial: "{{ serial }}"
    hostname: "{{ hostname }}"

Questions on support

Disclaimer: I'm relatively new to Ansible

I see the galaxy.yml, so does this replace the modules built into Ansible and therefore I need to install the galaxy modules?

There a place to ask questions on using the modules? I'm seeing some strange behavior, but don't know that it's an issue or just bad configuration on my part.

Case of 'Any' in causes changes

Not sure if this is fixable, but having the improper case for 'Any' in CIDR and Port references on the firewall will always report a change. CIDR and Port need to be 'Any', but Meraki will accept 'any' and capitalize it for you. This causes the module to report 'changed' on every execution though, because it's changing the case.

Protocol must be 'any' and Meraki will error if you provide 'Any'. Could the module fix the case per Meraki's expectations before submitting to the API?

is_update_required() should handle different types to avoid traceback

is_update_required() no longer does a type check which means there can be a segmentation fault if .items() is used on objects other than a dict. Exception handling should fix this problem.

TASK [meraki_content_filtering : Set blocked URL category using net_id] ********
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: AttributeError: 'str' object has no attribute 'items'
fatal: [localhost]: FAILED! => {"changed": false, "module_stderr": "Traceback (most recent call last):\n  File \"/root/.ansible/tmp/ansible-tmp-1577141540.0925586-253906176221639/AnsiballZ_meraki_content_filtering.py\", line 116, in <module>\n    _ansiballz_main()\n  File \"/root/.ansible/tmp/ansible-tmp-1577141540.0925586-253906176221639/AnsiballZ_meraki_content_filtering.py\", line 108, in _ansiballz_main\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n  File \"/root/.ansible/tmp/ansible-tmp-1577141540.0925586-253906176221639/AnsiballZ_meraki_content_filtering.py\", line 54, in invoke_module\n    runpy.run_module(mod_name='ansible_collections.cisco.meraki.plugins.modules.meraki_content_filtering', init_globals=None, run_name='__main__', alter_sys=True)\n  File \"/usr/lib/python3.6/runpy.py\", line 205, in run_module\n    return _run_module_code(code, init_globals, run_name, mod_spec)\n  File \"/usr/lib/python3.6/runpy.py\", line 96, in _run_module_code\n    mod_name, mod_spec, pkg_name, script_name)\n  File \"/usr/lib/python3.6/runpy.py\", line 85, in _run_code\n    exec(code, run_globals)\n  File \"/tmp/ansible_meraki_content_filtering_payload_x_7y2c19/ansible_meraki_content_filtering_payload.zip/ansible_collections/cisco/meraki/plugins/modules/meraki_content_filtering.py\", line 248, in <module>\n  File \"/tmp/ansible_meraki_content_filtering_payload_x_7y2c19/ansible_meraki_content_filtering_payload.zip/ansible_collections/cisco/meraki/plugins/modules/meraki_content_filtering.py\", line 221, in main\n  File \"/tmp/ansible_meraki_content_filtering_payload_x_7y2c19/ansible_meraki_content_filtering_payload.zip/ansible_collections/cisco/meraki/plugins/module_utils/network/meraki/meraki.py\", line 245, in is_update_required\n  File \"/tmp/ansible_meraki_content_filtering_payload_x_7y2c19/ansible_meraki_content_filtering_payload.zip/ansible_collections/cisco/meraki/plugins/module_utils/network/meraki/meraki.py\", line 238, in is_update_required\n  File \"/tmp/ansible_meraki_content_filtering_payload_x_7y2c19/ansible_meraki_content_filtering_payload.zip/ansible_collections/cisco/meraki/plugins/module_utils/network/meraki/meraki.py\", line 242, in is_update_required\nAttributeError: 'str' object has no attribute 'items'\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}

meraki_admin - KeyError: 'network'

When trying to create an admin with network(s) assigned during creation, I'm receiving the following error:

Traceback (most recent call last):
File "/home/steve/.ansible/tmp/ansible-tmp-1592414226.5026495-265972714452428/AnsiballZ_meraki_admin.py", line 102, in
_ansiballz_main()
File "/home/steve/.ansible/tmp/ansible-tmp-1592414226.5026495-265972714452428/AnsiballZ_meraki_admin.py", line 94, in _ansiballz_main
invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
File "/home/steve/.ansible/tmp/ansible-tmp-1592414226.5026495-265972714452428/AnsiballZ_meraki_admin.py", line 40, in invoke_module
runpy.run_module(mod_name='ansible.modules.network.meraki.meraki_admin', init_globals=None, run_name='main', alter_sys=True)
File "/usr/lib/python3.8/runpy.py", line 206, in run_module
return _run_module_code(code, init_globals, run_name, mod_spec)
File "/usr/lib/python3.8/runpy.py", line 96, in _run_module_code
_run_code(code, mod_globals, init_globals,
File "/usr/lib/python3.8/runpy.py", line 86, in _run_code
exec(code, run_globals)
File "/tmp/ansible_meraki_admin_payload_2hzpculp/ansible_meraki_admin_payload.zip/ansible/modules/network/meraki/meraki_admin.py", line 498, in
File "/tmp/ansible_meraki_admin_payload_2hzpculp/ansible_meraki_admin_payload.zip/ansible/modules/network/meraki/meraki_admin.py", line 470, in main
File "/tmp/ansible_meraki_admin_payload_2hzpculp/ansible_meraki_admin_payload.zip/ansible/modules/network/meraki/meraki_admin.py", line 336, in create_admin
File "/tmp/ansible_meraki_admin_payload_2hzpculp/ansible_meraki_admin_payload.zip/ansible/modules/network/meraki/meraki_admin.py", line 316, in network_factory
KeyError: 'network'
fatal: [localhost]: FAILED! => {
"changed": false,
"module_stderr": "Traceback (most recent call last):\n File "/home/steve/.ansible/tmp/ansible-tmp-1592414226.5026495-265972714452428/AnsiballZ_meraki_admin.py", line 102, in \n _ansiballz_main()\n File "/home/steve/.ansible/tmp/ansible-tmp-1592414226.5026495-265972714452428/AnsiballZ_meraki_admin.py", line 94, in _ansiballz_main\n invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n File "/home/steve/.ansible/tmp/ansible-tmp-1592414226.5026495-265972714452428/AnsiballZ_meraki_admin.py", line 40, in invoke_module\n runpy.run_module(mod_name='ansible.modules.network.meraki.meraki_admin', init_globals=None, run_name='main', alter_sys=True)\n File "/usr/lib/python3.8/runpy.py", line 206, in run_module\n return _run_module_code(code, init_globals, run_name, mod_spec)\n File "/usr/lib/python3.8/runpy.py", line 96, in _run_module_code\n _run_code(code, mod_globals, init_globals,\n File "/usr/lib/python3.8/runpy.py", line 86, in _run_code\n exec(code, run_globals)\n File "/tmp/ansible_meraki_admin_payload_2hzpculp/ansible_meraki_admin_payload.zip/ansible/modules/network/meraki/meraki_admin.py", line 498, in \n File "/tmp/ansible_meraki_admin_payload_2hzpculp/ansible_meraki_admin_payload.zip/ansible/modules/network/meraki/meraki_admin.py", line 470, in main\n File "/tmp/ansible_meraki_admin_payload_2hzpculp/ansible_meraki_admin_payload.zip/ansible/modules/network/meraki/meraki_admin.py", line 336, in create_admin\n File "/tmp/ansible_meraki_admin_payload_2hzpculp/ansible_meraki_admin_payload.zip/ansible/modules/network/meraki/meraki_admin.py", line 316, in network_factory\nKeyError: 'network'\n",
"module_stdout": "",
"msg": "MODULE FAILURE\nSee stdout/stderr for the exact error",
"rc": 1
}

Playbook:


  • name: Meraki Playbook - Create User
    connection: local
    hosts: localhost

    tasks:

    • name: Create a new administrator with full access to a network
      meraki_admin:
      auth_key:
      org_id:
      state: present
      name: Test User
      org_access: none
      email: [email protected]
      networks:
      - id: N_12345XXXXXXXXXXX
      access: full

vlan assignment doesn't work when configured SSID's on an MX

When I run the playbook to configure SSID's on the MX, everything works fine bar the vlan assignment, which seems to be assigned to each SSID by whatever the lowest SVI vlan ID number is set is.

    # create the guest wireless ssid
    - name: enable guest ssid
      meraki_ssid:
        auth_key: "{{   auth_key  }}"
        state: present
        org_name: "{{  org_name  }}"
        net_name: "{{  network_name  }}"
        name: "Wireless-Guest"
        vlan_id: 34
        auth_mode: "psk"
        psk: "password"
        encryption_mode: "wpa"
        enabled: true
      delegate_to: localhost
      tags:
        - "wireless"
    # create the secure wireless ssid
    - name: enable secure ssid
      meraki_ssid:
        auth_key: "{{   auth_key  }}"
        state: present
        org_name: "{{  org_name  }}"
        net_name: "{{  network_name  }}"
        name: "Wireless-Secure"
        vlan_id: 100
        auth_mode: "psk"
        psk: "password"
        encryption_mode: "wpa"        
        enabled: true
      delegate_to: localhost
      tags:
        - "wireless"

image

Is there a way to set the vlan that isn't vlan_id?

meraki_config_template - Integration test crashing with NoneType

TASK [meraki_config_template : Unbind a template from a network via id with check mode] ***
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: TypeError: 'NoneType' object is not iterable
fatal: [localhost]: FAILED! => {"changed": false, "module_stderr": "Traceback (most recent call last):\n  File \"/root/.ansible/tmp/ansible-tmp-1577198010.4284434-92791456721565/AnsiballZ_meraki_config_template.py\", line 116, in <module>\n    _ansiballz_main()\n  File \"/root/.ansible/tmp/ansible-tmp-1577198010.4284434-92791456721565/AnsiballZ_meraki_config_template.py\", line 108, in _ansiballz_main\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n  File \"/root/.ansible/tmp/ansible-tmp-1577198010.4284434-92791456721565/AnsiballZ_meraki_config_template.py\", line 54, in invoke_module\n    runpy.run_module(mod_name='ansible_collections.cisco.meraki.plugins.modules.meraki_config_template', init_globals=None, run_name='__main__', alter_sys=True)\n  File \"/usr/lib/python3.6/runpy.py\", line 205, in run_module\n    return _run_module_code(code, init_globals, run_name, mod_spec)\n  File \"/usr/lib/python3.6/runpy.py\", line 96, in _run_module_code\n    mod_name, mod_spec, pkg_name, script_name)\n  File \"/usr/lib/python3.6/runpy.py\", line 85, in _run_code\n    exec(code, run_globals)\n  File \"/tmp/ansible_meraki_config_template_payload_e71pcbhx/ansible_meraki_config_template_payload.zip/ansible_collections/cisco/meraki/plugins/modules/meraki_config_template.py\", line 333, in <module>\n  File \"/tmp/ansible_meraki_config_template_payload_e71pcbhx/ansible_meraki_config_template_payload.zip/ansible_collections/cisco/meraki/plugins/modules/meraki_config_template.py\", line 298, in main\n  File \"/tmp/ansible_meraki_config_template_payload_e71pcbhx/ansible_meraki_config_template_payload.zip/ansible_collections/cisco/meraki/plugins/modules/meraki_config_template.py\", line 143, in is_template_valid\nTypeError: 'NoneType' object is not iterable\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}

MX Static Routes not idempotent

Static routes on the MX error if already exist. Script below executes correctly if static routes don't exist.

Play:

- name: Create Domain Controllers
      meraki_static_route:
        state: present
        org_name: REDACTED ORG
        net_name: REDACTED NAME
        name: Domain Controllers
        subnet: 10.23.1.0/24
        gateway_ip: 10.10.1.2
      register: create_route
      until: create_route is not failed
      retries: 3

Error:

<127.0.0.1> EXEC /bin/sh -c 'echo ~root && sleep 0'
 <127.0.0.1> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /root/.ansible/tmp/ansible-tmp-1582142494.4906743-254991428343632 `" && echo ansible-tmp-1582142494.4906743-254991428343632="` echo /root/.ansible/tmp/ansible-tmp-1582142494.4906743-254991428343632 `" ) && sleep 0'
 Using module file /root/.ansible/collections/ansible_collections/cisco/meraki/plugins/modules/meraki_static_route.py
 <127.0.0.1> PUT /root/.ansible/tmp/ansible-local-786ejx9ih_z/tmpl13fm7jd TO /root/.ansible/tmp/ansible-tmp-1582142494.4906743-254991428343632/AnsiballZ_meraki_static_route.py
 <127.0.0.1> EXEC /bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1582142494.4906743-254991428343632/ /root/.ansible/tmp/ansible-tmp-1582142494.4906743-254991428343632/AnsiballZ_meraki_static_route.py && sleep 0'
 <127.0.0.1> EXEC /bin/sh -c '/usr/bin/python3 /root/.ansible/tmp/ansible-tmp-1582142494.4906743-254991428343632/AnsiballZ_meraki_static_route.py && sleep 0'
 <127.0.0.1> EXEC /bin/sh -c 'rm -f -r /root/.ansible/tmp/ansible-tmp-1582142494.4906743-254991428343632/ > /dev/null 2>&1 && sleep 0'
 The full traceback is:
   File "/tmp/ansible_meraki_static_route_payload_k_ejwl9e/ansible_meraki_static_route_payload.zip/ansible_collections/cisco/meraki/plugins/module_utils/network/meraki/meraki.py", line 69, in inner
     raise HTTPError("")
 fatal: [localhost]: FAILED! => {
     "attempts": 3,
     "body": {
         "errors": [
             "Static lan route subnets cannot have the same subnet."
         ]
     },
     "changed": false,
     "invocation": {
         "module_args": {
             "auth_key": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
             "enabled": null,
             "fixed_ip_assignments": null,
             "follow_redirects": "all",
             "gateway_ip": "10.10.1.2",
             "host": "api.meraki.com",
             "internal_error_retry_time": 60,
             "name": "Domain Controllers",
             "net_id": null,
             "net_name": "REDACTED NAME",
             "org_id": null,
             "org_name": "REDACTED ORG",
             "output_format": "snakecase",
             "output_level": "normal",
             "protocol": "https",
             "rate_limit_retry_time": 165,
             "reserved_ip_ranges": null,
             "route_id": null,
             "state": "present",
             "subnet": "10.23.1.0/24",
             "timeout": 30,
             "use_https": true,
             "use_proxy": false,
             "validate_certs": true
         }
     },
     "msg": "HTTP error 400 - https://api.meraki.com/api/v0/networks/L_633881647552404693/staticRoutes/ - ['Static lan route subnets cannot have the same subnet.']",
     "response": "HTTP Error 400: Bad Request",
     "status": 400
 }
 PLAY RECAP *********************************************************************
 localhost                  : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0  

meraki_firewalled_services - diff generation may not receive a dict

There is a corner case where the diff generation won't work and will crash because a list is passed to the recursive_diff method.

TASK [meraki_firewalled_services : Set icmp service to restricted] *************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: AttributeError: 'NoneType' object has no attribute 'items'
fatal: [localhost]: FAILED! => {"changed": false, "module_stderr": "Traceback (most recent call last):\n  File \"/root/.ansible/tmp/ansible-tmp-1577195241.629298-37882790845007/AnsiballZ_meraki_firewalled_services.py\", line 116, in <module>\n    _ansiballz_main()\n  File \"/root/.ansible/tmp/ansible-tmp-1577195241.629298-37882790845007/AnsiballZ_meraki_firewalled_services.py\", line 108, in _ansiballz_main\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n  File \"/root/.ansible/tmp/ansible-tmp-1577195241.629298-37882790845007/AnsiballZ_meraki_firewalled_services.py\", line 54, in invoke_module\n    runpy.run_module(mod_name='ansible_collections.cisco.meraki.plugins.modules.meraki_firewalled_services', init_globals=None, run_name='__main__', alter_sys=True)\n  File \"/usr/lib/python3.6/runpy.py\", line 205, in run_module\n    return _run_module_code(code, init_globals, run_name, mod_spec)\n  File \"/usr/lib/python3.6/runpy.py\", line 96, in _run_module_code\n    mod_name, mod_spec, pkg_name, script_name)\n  File \"/usr/lib/python3.6/runpy.py\", line 85, in _run_code\n    exec(code, run_globals)\n  File \"/tmp/ansible_meraki_firewalled_services_payload_fci61rdz/ansible_meraki_firewalled_services_payload.zip/ansible_collections/cisco/meraki/plugins/modules/meraki_firewalled_services.py\", line 241, in <module>\n  File \"/tmp/ansible_meraki_firewalled_services_payload_fci61rdz/ansible_meraki_firewalled_services_payload.zip/ansible_collections/cisco/meraki/plugins/modules/meraki_firewalled_services.py\", line 227, in main\n  File \"/tmp/ansible_meraki_firewalled_services_payload_fci61rdz/ansible_meraki_firewalled_services_payload.zip/ansible/module_utils/common/dict_transformations.py\", line 126, in recursive_diff\nAttributeError: 'NoneType' object has no attribute 'items'\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}

Enable DHCP and DHCP options with Meraki_vlan

Looking through the options for meraki_vlan, I do not see a way to enable DHCP on a vlan and configure it's options. Also, does meraki_vlan work against layer 3 meraki switches, or only the MX?

meraki_network is failing when state: present is set

I'm just now seeing an error with the meraki_network module having made no changes to my environment:

Playbook:

# tasks file for create_wireless_network
  - name: "Create Meraki Wireless - {{  site_code  }} network"
    meraki_network:
     auth_key: "{{  auth_key  }}"
     state: present
     org_name: "{{  org_name_wireless  }}"
     net_name: "Wireless - {{  site_code  }}"
     type: 
      - "wireless"
     timezone: "{{  time_zone  }}"
     tags: "{{  site_code  }}"
     internal_error_retry_time: 60
    delegate_to: localhost 
    tags:
     - create_wap_network

Output after playbook is run:

flynn@NCL-WL-16612:~/ansible-meraki$ ansible-playbook deploy_global_wireless_devices.yml --tags "create_wap_network" -vvv
ansible-playbook 2.9.6
  config file = /home/flynn/ansible-meraki/ansible.cfg
  configured module search path = [u'/home/flynn/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/dist-packages/ansible
  executable location = /usr/bin/ansible-playbook
  python version = 2.7.17 (default, Nov  7 2019, 10:07:09) [GCC 7.4.0]
Using /home/flynn/ansible-meraki/ansible.cfg as config file
host_list declined parsing /home/flynn/ansible-meraki/inventory as it did not pass its verify_file() method
script declined parsing /home/flynn/ansible-meraki/inventory as it did not pass its verify_file() method
auto declined parsing /home/flynn/ansible-meraki/inventory as it did not pass its verify_file() method
Parsed /home/flynn/ansible-meraki/inventory inventory source with ini plugin
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
Read vars_file './vault/meraki'
Read vars_file './group_vars/mr_CRIV.yml'
Read vars_file './vault/meraki'
Read vars_file './group_vars/mr_CRIV.yml'
Read vars_file './vault/meraki'
Read vars_file './group_vars/mr_CRIV.yml'
Read vars_file './vault/meraki'
Read vars_file './group_vars/mr_CRIV.yml'

PLAYBOOK: deploy_global_wireless_devices.yml *************************************************************************************************************
1 plays in deploy_global_wireless_devices.yml
Read vars_file './vault/meraki'
Read vars_file './group_vars/mr_CRIV.yml'
Read vars_file './vault/meraki'
Read vars_file './group_vars/mr_CRIV.yml'
Read vars_file './vault/meraki'
Read vars_file './group_vars/mr_CRIV.yml'

PLAY [deploy wireless devices] ***************************************************************************************************************************
META: ran handlers
Read vars_file './vault/meraki'
Read vars_file './group_vars/mr_CRIV.yml'
Read vars_file './vault/meraki'
Read vars_file './group_vars/mr_CRIV.yml'

TASK [create_wireless_network : Create Meraki Wireless - RDU network] ************************************************************************************
task path: /home/flynn/ansible-meraki/roles/create_wireless_network/tasks/main.yml:3
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: flynn
<localhost> EXEC /bin/sh -c 'echo ~flynn && sleep 0'
<localhost> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/flynn/.ansible/tmp/ansible-tmp-1588882510.32-50386924668295 `" && echo ansible-tmp-1588882510.32-50386924668295="` echo /home/flynn/.ansible/tmp/ansible-tmp-1588882510.32-50386924668295 `" ) && sleep 0'
Using module file /usr/lib/python2.7/dist-packages/ansible/modules/network/meraki/meraki_network.py
<localhost> PUT /home/flynn/.ansible/tmp/ansible-local-9105O2Da0L/tmp6Fu3fC TO /home/flynn/.ansible/tmp/ansible-tmp-1588882510.32-50386924668295/AnsiballZ_meraki_network.py
<localhost> EXEC /bin/sh -c 'chmod u+x /home/flynn/.ansible/tmp/ansible-tmp-1588882510.32-50386924668295/ /home/flynn/.ansible/tmp/ansible-tmp-1588882510.32-50386924668295/AnsiballZ_meraki_network.py && sleep 0'
<localhost> EXEC /bin/sh -c '/usr/bin/python2 /home/flynn/.ansible/tmp/ansible-tmp-1588882510.32-50386924668295/AnsiballZ_meraki_network.py && sleep 0'
<localhost> EXEC /bin/sh -c 'rm -f -r /home/flynn/.ansible/tmp/ansible-tmp-1588882510.32-50386924668295/ > /dev/null 2>&1 && sleep 0'
The full traceback is:
Traceback (most recent call last):
  File "/home/flynn/.ansible/tmp/ansible-tmp-1588882510.32-50386924668295/AnsiballZ_meraki_network.py", line 102, in <module>
    _ansiballz_main()
  File "/home/flynn/.ansible/tmp/ansible-tmp-1588882510.32-50386924668295/AnsiballZ_meraki_network.py", line 94, in _ansiballz_main
    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
  File "/home/flynn/.ansible/tmp/ansible-tmp-1588882510.32-50386924668295/AnsiballZ_meraki_network.py", line 40, in invoke_module
    runpy.run_module(mod_name='ansible.modules.network.meraki.meraki_network', init_globals=None, run_name='__main__', alter_sys=True)
  File "/usr/lib/python2.7/runpy.py", line 188, in run_module
    fname, loader, pkg_name)
  File "/usr/lib/python2.7/runpy.py", line 82, in _run_module_code
    mod_name, mod_fname, mod_loader, pkg_name)
  File "/usr/lib/python2.7/runpy.py", line 72, in _run_code
    exec code in run_globals
  File "/tmp/ansible_meraki_network_payload_nOAWVB/ansible_meraki_network_payload.zip/ansible/modules/network/meraki/meraki_network.py", line 390, in <module>
  File "/tmp/ansible_meraki_network_payload_nOAWVB/ansible_meraki_network_payload.zip/ansible/modules/network/meraki/meraki_network.py", line 371, in main
  File "/tmp/ansible_meraki_network_payload_nOAWVB/ansible_meraki_network_payload.zip/ansible/module_utils/network/meraki/meraki.py", line 116, in inner
ansible.module_utils.network.meraki.meraki.HTTPError: HTTP error 400 - None
fatal: [localhost -> localhost]: FAILED! => changed=false 
  module_stderr: |-
    Traceback (most recent call last):
      File "/home/flynn/.ansible/tmp/ansible-tmp-1588882510.32-50386924668295/AnsiballZ_meraki_network.py", line 102, in <module>
        _ansiballz_main()
      File "/home/flynn/.ansible/tmp/ansible-tmp-1588882510.32-50386924668295/AnsiballZ_meraki_network.py", line 94, in _ansiballz_main
        invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
      File "/home/flynn/.ansible/tmp/ansible-tmp-1588882510.32-50386924668295/AnsiballZ_meraki_network.py", line 40, in invoke_module
        runpy.run_module(mod_name='ansible.modules.network.meraki.meraki_network', init_globals=None, run_name='__main__', alter_sys=True)
      File "/usr/lib/python2.7/runpy.py", line 188, in run_module
        fname, loader, pkg_name)
      File "/usr/lib/python2.7/runpy.py", line 82, in _run_module_code
        mod_name, mod_fname, mod_loader, pkg_name)
      File "/usr/lib/python2.7/runpy.py", line 72, in _run_code
        exec code in run_globals
      File "/tmp/ansible_meraki_network_payload_nOAWVB/ansible_meraki_network_payload.zip/ansible/modules/network/meraki/meraki_network.py", line 390, in <module>
      File "/tmp/ansible_meraki_network_payload_nOAWVB/ansible_meraki_network_payload.zip/ansible/modules/network/meraki/meraki_network.py", line 371, in main
      File "/tmp/ansible_meraki_network_payload_nOAWVB/ansible_meraki_network_payload.zip/ansible/module_utils/network/meraki/meraki.py", line 116, in inner
    ansible.module_utils.network.meraki.meraki.HTTPError: HTTP error 400 - None
  module_stdout: ''
  msg: |-
    MODULE FAILURE
    See stdout/stderr for the exact error
  rc: 1

PLAY RECAP ***********************************************************************************************************************************************
localhost                  : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

This has been working fine for a long time so i'm wondering is something has changed at the API end? I've tested this on a few systems.

configure mx interface ports via ansible

Is there a way to configure the interface ports on an MX? They seem to default to trunk vlan 1 and it would be really nice if you could configure them to use the vlans you create?

image

Ansible not updating MX L3 Firewall, but states success

A simple playbook to update firewall rules on an MX device fails to to change, but Ansible reports success and no change. No change occurs on the Meraki Dashboard either. This playbook was used to generate the "Ansible Block traffic to server2" rule, so it did work at one point.

Playbook:

---

- name: Configure network settings for Meraki MX250 in Headquarters
  hosts: localhost
  collections:
    - cisco.meraki
  tasks:
    - name: Create firewall rules
      meraki_mx_l3_firewall:
        #org_id: '1234'
        #net_id: '1234'
        org_name: REDACTED ORG
        net_name: REDACTED NETWORK NAME
        state: present
        rules:
          - comment: Ansible Block traffic to server3
            src_cidr: 192.168.1.1/32,192.168.1.2/32
            src_port: any
            dest_cidr: 192.168.2.2/32
            dest_port: any
            protocol: any
            policy: deny

Output:

$ ansible-playbook -vvv playbooks/pb_meraki_hqmx_firewall.yml
 ansible-playbook 2.9.4
   config file = /builds/browngb/wci-headquarters-infrastructure/ansible.cfg
   configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
   ansible python module location = /usr/local/lib/python3.6/dist-packages/ansible
   executable location = /usr/local/bin/ansible-playbook
   python version = 3.6.9 (default, Nov  7 2019, 10:44:02) [GCC 8.3.0]
 Using /builds/browngb/wci-headquarters-infrastructure/ansible.cfg as config file
 vmware_vm_inventory declined parsing /builds/browngb/wci-headquarters-infrastructure/environments/prod/hosts as it did not pass its verify_file() method
 script declined parsing /builds/browngb/wci-headquarters-infrastructure/environments/prod/hosts as it did not pass its verify_file() method
 Parsed /builds/browngb/wci-headquarters-infrastructure/environments/prod/hosts inventory source with ini plugin
 PLAYBOOK: pb_meraki_hqmx_firewall.yml ******************************************
 1 plays in playbooks/pb_meraki_hqmx_firewall.yml
 PLAY [Configure network settings for Meraki MX250 in Headquarters] *************
 TASK [Gathering Facts] *********************************************************
 task path: /builds/browngb/wci-headquarters-infrastructure/playbooks/pb_meraki_hqmx_firewall.yml:7
 <127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: root
 <127.0.0.1> EXEC /bin/sh -c 'echo ~root && sleep 0'
 <127.0.0.1> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /root/.ansible/tmp/ansible-tmp-1580999540.4487858-86641614080556 `" && echo ansible-tmp-1580999540.4487858-86641614080556="` echo /root/.ansible/tmp/ansible-tmp-1580999540.4487858-86641614080556 `" ) && sleep 0'
 Using module file /usr/local/lib/python3.6/dist-packages/ansible/modules/system/setup.py
 <127.0.0.1> PUT /root/.ansible/tmp/ansible-local-10_tlojw82/tmpwi996fgu TO /root/.ansible/tmp/ansible-tmp-1580999540.4487858-86641614080556/AnsiballZ_setup.py
 <127.0.0.1> EXEC /bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1580999540.4487858-86641614080556/ /root/.ansible/tmp/ansible-tmp-1580999540.4487858-86641614080556/AnsiballZ_setup.py && sleep 0'
 <127.0.0.1> EXEC /bin/sh -c '/usr/bin/python3 /root/.ansible/tmp/ansible-tmp-1580999540.4487858-86641614080556/AnsiballZ_setup.py && sleep 0'
 <127.0.0.1> EXEC /bin/sh -c 'rm -f -r /root/.ansible/tmp/ansible-tmp-1580999540.4487858-86641614080556/ > /dev/null 2>&1 && sleep 0'
 ok: [localhost]
 META: ran handlers
 TASK [Create firewall rules] ***************************************************
 task path: /builds/browngb/wci-headquarters-infrastructure/playbooks/pb_meraki_hqmx_firewall.yml:12
 <127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: root
 <127.0.0.1> EXEC /bin/sh -c 'echo ~root && sleep 0'
 <127.0.0.1> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /root/.ansible/tmp/ansible-tmp-1580999541.1921477-107598852911355 `" && echo ansible-tmp-1580999541.1921477-107598852911355="` echo /root/.ansible/tmp/ansible-tmp-1580999541.1921477-107598852911355 `" ) && sleep 0'
 Using module file /root/.ansible/collections/ansible_collections/cisco/meraki/plugins/modules/meraki_mx_l3_firewall.py
 <127.0.0.1> PUT /root/.ansible/tmp/ansible-local-10_tlojw82/tmp3puw0lu4 TO /root/.ansible/tmp/ansible-tmp-1580999541.1921477-107598852911355/AnsiballZ_meraki_mx_l3_firewall.py
 <127.0.0.1> EXEC /bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1580999541.1921477-107598852911355/ /root/.ansible/tmp/ansible-tmp-1580999541.1921477-107598852911355/AnsiballZ_meraki_mx_l3_firewall.py && sleep 0'
 <127.0.0.1> EXEC /bin/sh -c '/usr/bin/python3 /root/.ansible/tmp/ansible-tmp-1580999541.1921477-107598852911355/AnsiballZ_meraki_mx_l3_firewall.py && sleep 0'
 <127.0.0.1> EXEC /bin/sh -c 'rm -f -r /root/.ansible/tmp/ansible-tmp-1580999541.1921477-107598852911355/ > /dev/null 2>&1 && sleep 0'
 ok: [localhost] => {
     "changed": false,
     "data": [
         {
             "comment": "Ansible Block traffic to server2",
             "dest_cidr": "192.168.2.2/32",
             "dest_port": "Any",
             "policy": "deny",
             "protocol": "any",
             "src_cidr": "192.168.1.0/24",
             "src_port": "Any",
             "syslog_enabled": false
         },
         {
             "comment": "Default rule",
             "dest_cidr": "Any",
             "dest_port": "Any",
             "policy": "allow",
             "protocol": "Any",
             "src_cidr": "Any",
             "src_port": "Any",
             "syslog_enabled": false
         }
     ],
     "invocation": {
         "module_args": {
             "auth_key": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
             "follow_redirects": "all",
             "host": "api.meraki.com",
             "internal_error_retry_time": 60,
             "net_id": null,
             "net_name": "REDACTED NETWORK NAME",
             "org_id": null,
             "org_name": "REDACTED ORG",
             "output_format": "snakecase",
             "output_level": "normal",
             "protocol": "https",
             "rate_limit_retry_time": 165,
             "rules": [
                 {
                     "comment": "Ansible Block traffic to server3",
                     "dest_cidr": "192.168.2.2/32",
                     "dest_port": "any",
                     "policy": "deny",
                     "protocol": "any",
                     "src_cidr": "192.168.1.1/32,192.168.1.2/32",
                     "src_port": "any",
                     "syslog_enabled": false
                 }
             ],
             "state": "present",
             "syslog_default_rule": null,
             "timeout": 30,
             "use_https": true,
             "use_proxy": false,
             "validate_certs": true
         }
     },
     "response": "OK (unknown bytes)",
     "status": 200
 }
 META: ran handlers
 META: ran handlers
 PLAY RECAP *********************************************************************
 localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
 Job succeeded

Rename modules to include product line

Consider renaming the modules to include the targeted hardware in the title.

meraki_admin.py => meraki_org_admin.py
meraki_config_template.py => meraki_org_config_template.py
meraki_content_filtering.py => meraki_mx_content_filtering.py
meraki_device.py => meraki_network_device.py
meraki_firewalled_services.py => meraki_network_firewalled_services.py
meraki_intrusion_prevention.py => meraki_mx_intrusion_prevention.py
meraki_malware.py =>meraki_network_malware.py
meraki_management_interface.py = meraki_network_management_interface.py
meraki_mr_l3_firewall.py => same
meraki_ms_link_aggregation.py => same
meraki_mx_l3_firewall.py => same
meraki_mx_l7_firewall.py => same
meraki_mx_site_to_site_firewall.py => same
meraki_mx_uplink.py => same
meraki_nat.py => meraki_mx_nat.py
meraki_network.py => same
meraki_organization.py => same
meraki_site_to_site_vpn.py => meraki_network_site_to_site.vpn.py
meraki_snmp.py => meraki_network_snmp.py
meraki_ssid.py => meraki_mr_ssid.py
meraki_static_route.py => meraki_mx_static_route.py
meraki_switch_access_list.py => meraki_ms_access_list.py
meraki_switch_stack.py => meraki_ms_stack.py
meraki_switch_storm_control.py => meraki_ms_storm.control.py
meraki_switchport.py => meraki_ms_switchport.py
meraki_syslog.py = meraki_network_syslog.py
meraki_vlan.py = meraki_mx_vlan.py
meraki_webhook.py = meraki_network_webhook.py

Enable support for check mode in all modules

  • meraki_admin.py
  • meraki_config_template.py
  • meraki_content_filtering.py
  • meraki_device.py
  • meraki_firewalled_services.py
  • meraki_malware.py
  • meraki_mr_l3_firewall.py
  • meraki_mx_l3_firewall.py
  • meraki_mx_l7_firewall.py
  • meraki_nat.py
  • meraki_network.py
  • meraki_organization.py
  • meraki_snmp.py
  • meraki_ssid.py
  • meraki_static_route.py
  • meraki_switch_access_list.py
  • meraki_switch_storm_control.py
  • meraki_switchport.py
  • meraki_syslog.py
  • meraki_vlan.py
  • meraki_webhook.py

Template for inventory.networking

A template for the inventory.networking file should be distributed with the collection. Otherwise, no one knows what would go in it.

Error when connecting spoke end of site to site vpn

I'm seeing this error when trying to connect the spoke end of a site to site vpn config:

TASK [configure site to site vpn spoke settings] ***************************************************************************************************************************
fatal: [127.0.0.1 -> localhost]: FAILED! => changed=false 
  msg: 'HTTP error 400 - https://api.meraki.com/api/v1/networks/N_691865492754832626/appliance/vpn/siteToSiteVpn/ - Invalid hub: Chris-Wood-Office'
  response: OK (unknown bytes)
  status: 400

I'm assuming that the ID is something other than the one presented in the dropdown when manually configuring these settings?
image

If that's the case I would have assumed that querying the hub settings would show the ID, but they don't:

TASK [print meraki vpn hub settings] ***************************************************************************************************************************************
ok: [127.0.0.1] => 
  query_all_hub:
    changed: false
    data:
      hubs: []
      mode: hub
      subnets:
      - local_subnet: 
        use_vpn: false
      - local_subnet: 
        use_vpn: false
      - local_subnet: 
        use_vpn: true
    failed: false
    response: OK (unknown bytes)
    status: 200

My code for the spoke end is here:

    # sets the mx vpn site to site tunnel config
    - name: configure site to site vpn spoke settings
      meraki_site_to_site_vpn:
        auth_key: "{{   auth_key  }}"
        org_name: "{{  org_name  }}"
        net_name: "{{  network_name  }}"
        state: present
        mode: "{{  endpoint_mode  }}"
        hubs:
          "{{  vpn_spoke_settings  }}"
        subnets:
          "{{  vpn_subnets  }}"    
      delegate_to: localhost
      when: endpoint_mode == "spoke"
      register: set_hub
      tags:
        - "site_to_site"

Configuring the hub works fine using this method.

Create diff generation method

Much of the diff creation code in the modules is duplicated. The Meraki class should have a generate_diff method which does the work in a centralized and consistent manner

  • meraki_admin.py
  • meraki_config_template.py
  • meraki_content_filtering.py
  • meraki_device.py
  • meraki_firewalled_services.py
  • meraki_malware.py
  • meraki_mr_l3_firewall.py
  • meraki_mx_l3_firewall.py
  • meraki_mx_l7_firewall.py
  • meraki_nat.py
  • meraki_network.py
  • meraki_organization.py
  • meraki_snmp.py
  • meraki_ssid.py
  • meraki_static_route.py
  • meraki_switchport.py
  • meraki_syslog.py
  • meraki_vlan.py
  • meraki_webhook.py

Inclusion of cisco.meraki in Ansible 2.10

This collection will be included in Ansible 2.10 because it contains modules and/or plugins that were included in Ansible 2.9. Please review:

DEADLINE: 2020-08-18

The latest version of the collection available on August 18 will be included in Ansible 2.10.0, except possibly newer versions which differ only in the patch level. (For details, see the roadmap). Please release version 1.0.0 of your collection by this date! If 1.0.0 does not exist, the same 0.x.y version will be used in all of Ansible 2.10 without updates, and your 1.x.y release will not be included until Ansible 2.11 (unless you request an exception at a community working group meeting and go through a demanding manual process to vouch for backwards compatibility . . . you want to avoid this!).

Follow semantic versioning rules

Your collection versioning must follow all semver rules. This means:

  • Patch level releases can only contain bugfixes;
  • Minor releases can contain new features, new modules and plugins, and bugfixes, but must not break backwards compatibility;
  • Major releases can break backwards compatibility.

Changelogs and Porting Guide

Your collection should provide data for the Ansible 2.10 changelog and porting guide. The changelog and porting guide are automatically generated from ansible-base, and from the changelogs of the included collections. All changes from the breaking_changes, major_changes, removed_features and deprecated_features sections will appear in both the changelog and the porting guide. You have two options for providing changelog fragments to include:

  1. If possible, use the antsibull-changelog tool, which uses the same changelog fragment as the ansible/ansible repository (see the documentation).
  2. If you cannot use antsibull-changelog, you can provide the changelog in a machine-readable format as changelogs/changelog.yaml inside your collection (see the documentation of changelogs/changelog.yaml format).

If you cannot contribute to the integrated Ansible changelog using one of these methods, please provide a link to your collection's changelog by creating an issue in https://github.com/ansible-community/ansible-build-data/. If you do not provide changelogs/changelog.yml or a link, users will not be able to find out what changed in your collection from the Ansible changelog and porting guide.

Make sure your collection passes the sanity tests

Run ansible-test sanity --docker -v in the collection with the latest ansible-base or stable-2.10 ansible/ansible checkout.

Keep informed

Be sure you're subscribed to:

Questions and Feedback

If you have questions or want to provide feedback, please see the Feedback section in the collection requirements.

(Internal link to keep track of issues: ansible-collections/overview#102)

Update return documentation

The Meraki Dashboard API has evolved and return keys have been added. However, not all these new keys have been added to the return documentation.

  • meraki_admin
  • meraki_config_template
  • meraki_content_filtering
  • meraki_device
  • meraki_firewalled_services
  • meraki_intrusion_prevention
  • meraki_malware
  • meraki_management_interface
  • meraki_mr_l3_firewall
  • meraki_ms_link_aggregation
  • meraki_mr_l3_firewall
  • meraki_mr_l7_firewall
  • meraki_mx_site_to_site_firewall
  • meraki_mx_uplink
  • meraki_nat
  • meraki_network
  • meraki_site_to_site_vpn
  • meraki_snmp
  • meraki_ssid
  • meraki_static_routes
  • meraki_switch_access_list
  • meraki_switch_stack
  • meraki_storm_control
  • meraki_switchport
  • meraki_syslog
  • meraki_vlan
  • meraki_webhook

diff output may display password

When showing diff output, Ansible will hide secrets using the no_log parameter. However, the information will still show in the before data structure. This should be hidden as well.

Error in meraki_mx_l3_firewall

Submitted by Richard.

I want to create some l3 firewall rules with the meraki_mx_l3_firewall module.

Code:

- name: Creating outbund rule
    meraki_mx_l3_firewall:
      auth_key: "{{ api_key }}"
      org_id: "{{ org.org_id }}"
      net_name: "{{ net_name }}"
      state: present
      rules:
        - comment: some comment
          src_cidr: 192.168.128.0/24
          src_port: "{{src_port}}"  # any
          dest_cidr: 192.168.10.21/24
          dest_port: "{{ dest_port }}" # any
          protocol: "{{ protocol }}" # any
          policy: deny
    delegate_to: localhost
    register: output

with postman i can create the l3 firewall rule but not with the module.
I always get a HTTP 403 error. I added an screenshot of the output.
API-Key, org_id and net_name are correct.

local_status_page_enabled is not idempotent

It looks like the local_status_page_enabled and remote_status_page_enabled are not idempotent. so it changes always when you run the playbook API change logs:

Oct 01, 2020 15:11 Peter Tavenier     via API PUT /api/v1/networks/<FILTERED>/settings {"localStatusPageEnabled":false,"remoteStatusPageEnabled":false} {"localStatusPageEnabled":false,"remoteStatusPageEnabled":false}

when using task;

- name: disable local status page
  meraki_network:
    auth_key: "{{ MERAKI_API_KEY }}"
    org_id: "{{ meraki_org_id }}"
    state: present
    local_status_page_enabled: false
    net_name: "{{ item }}"
  with_items: "{{ xlsx_networks }}"
  tags: create_network

I get the message it is changed;

TASK [create_network : disable local status page] ********************************************************************************************************************************************************************************
task path: /home/peter/repositories/ansible/roles/create_network/tasks/main.yml:52
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: peter
<localhost> EXEC /bin/sh -c 'echo ~peter && sleep 0'
<localhost> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/peter/.ansible/tmp/ansible-tmp-1601564844.9802144-279683828170947 `" && echo ansible-tmp-1601564844.9802144-279683828170947="` echo /home/peter/.ansible/tmp/ansible-tmp-1601564844.9802144-279683828170947 `" ) && sleep 0'
Using module file /home/peter/.ansible/collections/ansible_collections/cisco/meraki/plugins/modules/meraki_network.py
<localhost> PUT /home/peter/.ansible/tmp/ansible-local-1732otvn8_sm/tmppwkkeh82 TO /home/peter/.ansible/tmp/ansible-tmp-1601564844.9802144-279683828170947/AnsiballZ_meraki_network.py
<localhost> EXEC /bin/sh -c 'chmod u+x /home/peter/.ansible/tmp/ansible-tmp-1601564844.9802144-279683828170947/ /home/peter/.ansible/tmp/ansible-tmp-1601564844.9802144-279683828170947/AnsiballZ_meraki_network.py && sleep 0'
<localhost> EXEC /bin/sh -c '/usr/bin/python3 /home/peter/.ansible/tmp/ansible-tmp-1601564844.9802144-279683828170947/AnsiballZ_meraki_network.py && sleep 0'
<localhost> EXEC /bin/sh -c 'rm -f -r /home/peter/.ansible/tmp/ansible-tmp-1601564844.9802144-279683828170947/ > /dev/null 2>&1 && sleep 0'
changed: [localhost] => (item=<FILTERED>) => {
    "ansible_loop_var": "item",
    "changed": true,
    "data": {
        "local_status_page_enabled": false,
        "remote_status_page_enabled": false
    },
    "invocation": {
        "module_args": {
            "auth_key": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "enable_vlans": null,
            "follow_redirects": "all",
            "host": "api.meraki.com",
            "internal_error_retry_time": 60,
            "local_status_page_enabled": false,
            "net_id": null,
            "net_name": "<FILTERED>",
            "org_id": "<FILTERED>",
            "org_name": null,
            "output_format": "snakecase",
            "output_level": "normal",
            "protocol": "https",
            "rate_limit_retry_time": 165,
            "remote_status_page_enabled": null,
            "state": "present",
            "tags": null,
            "timeout": 30,
            "timezone": null,
            "type": null,
            "use_https": true,
            "use_proxy": false,
            "validate_certs": true
        }
    },
    "item": "<FILTERED>",
    "response": "OK (unknown bytes)",
    "status": 200
}

first I thought is was because of the "remote_status_page_enabled": null,

but changing the task to:

- name: disable local status page
  meraki_network:
    auth_key: "{{ MERAKI_API_KEY }}"
    org_id: "{{ meraki_org_id }}"
    state: present
    local_status_page_enabled: false
    remote_status_page_enabled: false
    net_name: "{{ item }}"
  with_items: "{{ xlsx_networks }}"
  tags: create_network 

it also is status "changed";

changed: [localhost] => (item=<FILTERED>) => {
    "ansible_loop_var": "item",
    "changed": true,
    "data": {
        "local_status_page_enabled": false,
        "remote_status_page_enabled": false
    },
    "invocation": {
        "module_args": {
            "auth_key": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "enable_vlans": null,
            "follow_redirects": "all",
            "host": "api.meraki.com",
            "internal_error_retry_time": 60,
            "local_status_page_enabled": false,
            "net_id": null,
            "net_name": "<FILTERED>",
            "org_id": "<FILTERED>",
            "org_name": null,
            "output_format": "snakecase",
            "output_level": "normal",
            "protocol": "https",
            "rate_limit_retry_time": 165,
            "remote_status_page_enabled": false,
            "state": "present",
            "tags": null,
            "timeout": 30,
            "timezone": null,
            "type": null,
            "use_https": true,
            "use_proxy": false,
            "validate_certs": true
        }
    },
    "item": "<FILTERED>",
    "response": "OK (unknown bytes)",
    "status": 200
}

Collection Artifacts contain errant files

It appears as though the collection generated from this repo contains a full python virtual environment, as well as many other files that shouldn't be included in the build.

Some other examples:

  • .vscode/settings.json
  • tests/output/junit/meraki_site_to_site_vpn-tbo31dqq-1585418559.9924767.xml
  • .DS_Store

See the attached file for a full list.

cisco.meraki.txt

Rename meraki_mx_uplink

v1 of the API greatly improves the uplink configuration options on the MX platform. meraki_mx_uplink is too vague and should be renamed.

MX Static Routes not idempotent

Static routes on the MX error if already exist. Script below executes correctly if static routes don't exist.

Play:

- name: Create Domain Controllers
      meraki_static_route:
        state: present
        org_name: REDACTED ORG
        net_name: REDACTED NAME
        name: Domain Controllers
        subnet: 10.23.1.0/24
        gateway_ip: 10.10.1.2
      register: create_route
      until: create_route is not failed
      retries: 3

Error:

<127.0.0.1> EXEC /bin/sh -c 'echo ~root && sleep 0'
 <127.0.0.1> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /root/.ansible/tmp/ansible-tmp-1582142494.4906743-254991428343632 `" && echo ansible-tmp-1582142494.4906743-254991428343632="` echo /root/.ansible/tmp/ansible-tmp-1582142494.4906743-254991428343632 `" ) && sleep 0'
 Using module file /root/.ansible/collections/ansible_collections/cisco/meraki/plugins/modules/meraki_static_route.py
 <127.0.0.1> PUT /root/.ansible/tmp/ansible-local-786ejx9ih_z/tmpl13fm7jd TO /root/.ansible/tmp/ansible-tmp-1582142494.4906743-254991428343632/AnsiballZ_meraki_static_route.py
 <127.0.0.1> EXEC /bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1582142494.4906743-254991428343632/ /root/.ansible/tmp/ansible-tmp-1582142494.4906743-254991428343632/AnsiballZ_meraki_static_route.py && sleep 0'
 <127.0.0.1> EXEC /bin/sh -c '/usr/bin/python3 /root/.ansible/tmp/ansible-tmp-1582142494.4906743-254991428343632/AnsiballZ_meraki_static_route.py && sleep 0'
 <127.0.0.1> EXEC /bin/sh -c 'rm -f -r /root/.ansible/tmp/ansible-tmp-1582142494.4906743-254991428343632/ > /dev/null 2>&1 && sleep 0'
 The full traceback is:
   File "/tmp/ansible_meraki_static_route_payload_k_ejwl9e/ansible_meraki_static_route_payload.zip/ansible_collections/cisco/meraki/plugins/module_utils/network/meraki/meraki.py", line 69, in inner
     raise HTTPError("")
 fatal: [localhost]: FAILED! => {
     "attempts": 3,
     "body": {
         "errors": [
             "Static lan route subnets cannot have the same subnet."
         ]
     },
     "changed": false,
     "invocation": {
         "module_args": {
             "auth_key": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
             "enabled": null,
             "fixed_ip_assignments": null,
             "follow_redirects": "all",
             "gateway_ip": "10.10.1.2",
             "host": "api.meraki.com",
             "internal_error_retry_time": 60,
             "name": "Domain Controllers",
             "net_id": null,
             "net_name": "REDACTED NAME",
             "org_id": null,
             "org_name": "REDACTED ORG",
             "output_format": "snakecase",
             "output_level": "normal",
             "protocol": "https",
             "rate_limit_retry_time": 165,
             "reserved_ip_ranges": null,
             "route_id": null,
             "state": "present",
             "subnet": "10.23.1.0/24",
             "timeout": 30,
             "use_https": true,
             "use_proxy": false,
             "validate_certs": true
         }
     },
     "msg": "HTTP error 400 - https://api.meraki.com/api/v0/networks/L_633881647552404693/staticRoutes/ - ['Static lan route subnets cannot have the same subnet.']",
     "response": "HTTP Error 400: Bad Request",
     "status": 400
 }
 PLAY RECAP *********************************************************************
 localhost                  : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0  

meraki_nat - one to many isn't submitted

Integration test is showing one to many isn't being submitted with multiple rules.

TASK [meraki_nat : debug] ******************************************************
ok: [localhost] => {
    "create_multiple": {
        "changed": true,
        "data": {
            "port_forwarding": {
                "rules": [
                    {
                        "allowed_ips": [
                            "1.1.1.2"
                        ],
                        "lan_ip": "192.168.128.1",
                        "local_port": "11",
                        "name": "Test map",
                        "protocol": "tcp",
                        "public_port": "10",
                        "uplink": "both"
                    }
                ]
            }
        },
        "diff": {
            "after": {
                "port_forwarding": {
                    "rules": [
                        {
                            "allowedIps": [
                                "1.1.1.2"
                            ],
                            "lanIp": "192.168.128.1",
                            "localPort": "11",
                            "name": "Test map",
                            "protocol": "tcp",
                            "publicPort": "10",
                            "uplink": "both"
                        }
                    ]
                }
            },
            "before": {
                "port_forwarding": {
                    "rules": [
                        {
                            "allowedIps": [
                                "1.1.1.1"
                            ],
                            "lanIp": "192.168.128.1",
                            "localPort": "11",
                            "name": "Test map",
                            "protocol": "tcp",
                            "publicPort": "10",
                            "uplink": "both"
                        }
                    ]
                }
            }
        },
        "failed": false,
        "response": "OK (unknown bytes)",
        "status": 200
    }
}

TASK [meraki_nat : assert] *****************************************************
fatal: [localhost]: FAILED! => {
    "assertion": "create_multiple.data.one_to_many is defined",
    "changed": false,
    "evaluated_to": false,
    "msg": "Assertion failed"
}

loop in malware url whitelisting only takes last item

This very well might be me being crap, but I'm pretty sure this code should add a list of urls:

    # whitelist urls
    - name: if malware is enabled, set whitelisted urls
      meraki_malware:
        auth_key: "{{   auth_key  }}"
        org_name: "{{  org_name  }}"
        net_name: "{{  network_name  }}"
        state: present
        mode: "{{  enable_malware  }}"
        allowed_urls:
          - url: "{{  item.url  }}"
            comment: "{{  item.comment  }}"
      delegate_to: localhost
      with_items: "{{  whitelisted_urls  }}"
      when: malware.data.mode != "disabled"
      tags:
        - "malware"

var file

whitelisted_urls:
  - url: "www.google.co.uk"
    comment: "Google UK"
  - url: "www.google.com"
    comment: "Google US"
  - url: "www.yahoo.com"
    comment: "Yahoo US"

output

TASK [get malware settings] **********************************************************************************************************************************************
ok: [127.0.0.1 -> localhost]

TASK [if malware is enabled, set whitelisted urls] ***********************************************************************************************************************
changed: [127.0.0.1 -> localhost] => (item={'url': 'www.google.co.uk', 'comment': 'Google UK'})
changed: [127.0.0.1 -> localhost] => (item={'url': 'www.google.com', 'comment': 'Google US'})
changed: [127.0.0.1 -> localhost] => (item={'url': 'www.yahoo.com', 'comment': 'Yahoo US'})

PLAY RECAP ***************************************************************************************************************************************************************
127.0.0.1                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.