Working with Meraki API using CLI

Sometimes small problems are opportunities to learn something new or apply skills seldom used.

I often try to do things differently or new paths, sometimes just to avoid boredom, but also to improve myself. This is one of the pillars of essentialism and Kaizen that I try to apply to my work and life.

The challenge

For reasons I need to check very quick the public IP addresses of some Meraki MXs connected to LTE routers or dynamic IP addresses. Not all the devices have DDNS enabled so pinging the DNS name is not an option.

Of course the information is easy to find on the dashboard but what if we prefer use CLI instead of clicking on web pages?

The process

The only step to be performed on the Meraki dashboard is to enable API access and create the API key we’ll use to access.

Then open a terminal and set the API key as env variable:

gp@westworld:~$ export apikey=1234567890987654321

Now we can read the list of organizations we have access to:

curl --location --request GET "https://dashboard.meraki.com/api/v0/organizations" \
  --header "X-Cisco-Meraki-API-Key: $apikey"

The output will not be easy to read, let’s use jq to help:

curl --location --request GET "https://dashboard.meraki.com/api/v0/organizations" \
  --header "X-Cisco-Meraki-API-Key: $apikey" | jq .

Much better now, here’s the list of organization IDs and names:

[
    {
        "id": "637123",
        "name": "Customer 1"
    },
    {
        "id": "503123",
        "name": "Customer 2"
    },
    {
        "id": "774123",
        "name": "Customer 3"
    }
]

Now we can chose the organization we want to work with and set it as another env var:

gp@westworld:~$ export organizationId=637123

Then we collect the status of all the devices of the organization:

curl --location --request GET "https://dashboard.meraki.com/api/v0/organizations/$organizationId/deviceStatuses" \
  --header "X-Cisco-Meraki-API-Key: $apikey" | jq .

The output should look like this:

[
 {
    "name": "Network1",
    "serial": "Q2QN-aaaa-bbbb",
    "mac": "e0:cb:bc:aa:aa:aa",
    "publicIp": "123.123.14.116",
    "networkId": "L_596726951234567890",
    "status": "online",
    "usingCellularFailover": false,
    "wan1Ip": "10.2.101.3",
    "wan2Ip": null
  },
 {
    "name": "Network2",
    "serial": "Q2QN-cccc-dddd",
    "mac": "e0:cb:bc:aa:bb:cc",
    "publicIp": "82.193.120.22",
    "networkId": "N_596726950987654321",
    "status": "online",
    "usingCellularFailover": false,
    "wan1Ip": "10.211.139.4",
    "wan2Ip": "192.168.101.10"
  },
]

Notice that the public IP of the wan2 link is missing from the output, only the primary is present. We need to investigate further.

If the list od devices is too long we can filter the output using a specific network name using jq (notice the name on the filter):

curl --location --request GET "https://dashboard.meraki.com/api/v0/organizations/$organizationId/deviceStatuses" \
  --header "X-Cisco-Meraki-API-Key: $apikey" | jq '.[] | select(.name=="Network2")'

To make the command reusable I usually keep variable values out of the code, let’s do that again exporting the network name:

export networkname=Network2

curl --location --request GET "https://dashboard.meraki.com/api/v0/organizations/$organizationId/deviceStatuses" \
  --header "X-Cisco-Meraki-API-Key: $apikey" | jq '.[] | select(.name=="'$networkname'")'

Bask to the ouput, we need the serial of the device:

export serial=Q2QN-cccc-dddd

We can now query the uplink status for that particular MX firewall using its serial number an search key:

curl --location --request GET  "https://api.meraki.com/api/v0/networks/$netid/devices/$serial/uplink" \
  --header "X-Cisco-Meraki-API-Key: $apikey"| jq .

The output will contain private and public IP addresses of the wan links along with information about status, DNS etc:

[
  {
    "interface": "WAN 1",
    "status": "Active",
    "ip": "10.211.139.4",
    "gateway": "10.211.139.1",
    "publicIp": "82.193.120.22",
    "dns": "8.8.8.8, 8.8.4.4",
    "usingStaticIp": true
  },
  {
    "interface": "WAN 2",
    "status": "Ready",
    "ip": "192.168.101.10",
    "gateway": "192.168.101.1",
    "publicIp": "5.110.103.221",
    "dns": "8.8.8.8, 8.8.4.4",
    "usingStaticIp": true
  }
]

Well done!

Can we make it shorter?

Let’s try to improve this a little bit.

Let’s assume we start with information about API key, MX serial number and organization:

export apikey=1234567890987654321
export organizationId=637123
export serial=Q2QN-cccc-dddd

We need to find the network ID of the network the device is associated to. We can leverage bash scripting and jq to work for us, creating a new env variable matching the network id:

export networkid=$(curl --location --request GET "https://dashboard.meraki.com/api/v0/organizations/$organizationId/deviceStatuses" \
  --header "X-Cisco-Meraki-API-Key: $apikey" \
  | jq '.[] | select(.serial=="'$serial'")' \
  | jq -r '.networkId')

Check che result:

gp@westworld:~$ echo $networkid
N_596726950987654321

Now we can execute again the curl to get wan IP addresses:

curl --location --request GET  "https://api.meraki.com/api/v0/networks/$networkid/devices/$serial/uplink" \
  --header "X-Cisco-Meraki-API-Key: $apikey"| jq .

Can we try another approach?

Let’s try it again, starting with known API key, organization ID and network name.

export apikey=1234567890987654321
export organizationId=637123
export networkname=Network2

We need to find the network ID, filtering by network name we already have:

export networkid=$(curl --location --request GET "https://dashboard.meraki.com/api/v0/organizations/$organizationId/networks/" \
  --header "X-Cisco-Meraki-API-Key: $apikey" \
  | jq '.[] | select(.name=="'$networkname'")' \
  | jq -r '.id')

Verify it worked:

gp@westworld:~$ echo $networkid
N_596726950987654321

And now we can collect the well deserved results:

curl --location --request GET  "https://api.meraki.com/api/v0/networks/$networkid/devices/$serial/uplink" \
  --header "X-Cisco-Meraki-API-Key: $apikey"| jq .

If you’re scratching your head now it’s time to learn some bash scripting ;-)

Wrap up

This quick example shows how to use Meraki Dashboard API to run simple queries and find information avoiding the browser or writing too much code.

I’m currently running in production and as PoC a few projects to automate information collection and devices configuration leveraging the API with excellent results.

The Meraki Dashboard API documentation contains lots of examples. If you don’t have access to a Meraki test environment DevNet provides plenty of labs and learning materials.

Meraki DevNet developer hub

Meraki DevNet learning labs

Meraki Dasboard API

Meraki Github repository

jq is like sed for JSON data

Bash scripting cheat sheet

 
comments powered by Disqus