MERAKI BACKUP AND RESTORE

AndrewE
Here to help

Re: MERAKI BACKUP AND RESTORE

Where does this section belong?

 

try:
write_mx_vlans(file,meraki, row['id'])
except:
print("no mx VLAN")
try:
write_mx_cellular_fw_rules(file,meraki,row['id'])
except:
print("no mobile firewall rule")
try:
write_mx_l3_fw_rules(file,meraki,row['id'])
except:
print("no MX firewall rule")
try:
write_vpn_settings(file,meraki,row['id'])
except:
print("no VPN")
try:
write_ssid_settings(file,meraki,row['id'])
except:
print("no SSID")
try:
write_qos(file,meraki,row['id'])
except:
print("no QoS")
try:
write_mydevices(file,meraki,row['id'])
except:
print("no devices")

Fabian1
Here to help

Re: MERAKI BACKUP AND RESTORE

At the end, where you call the functions:

 

myNetworks = meraki.networks.get_organization_networks({"organization_id": orgid})
for row in myNetworks:
if row['type'] == 'systems manager':
continue

if row['tags'] is None:
del row['tags']

status="Processing network "+row['name']
print(status)
file.write("# Add Network: "+row['name']+"\n")
file.write("print('"+status+"')\n")

file.write("try:\n")
file.write("\t# https://dashboard.meraki.com/api_docs#create-a-network\n")
file.write("\tposturl = 'https://api.meraki.com/api/v0/organizations/{0}/networks'.format(str(orgid))\n")
file.write("\tdashboard = session.post(posturl, json="+repr(row)+", headers=headers)\n")
file.write("\tdashboard.raise_for_status()\n")

file.write("\tnetworkid=dashboard.json()['id']\n")
file.write("\n")
try:
write_mx_vlans(file,meraki, row['id'])
except:
print("no mx VLAN")
try:
write_mx_cellular_fw_rules(file,meraki,row['id'])
except:
print("no mobile firewall rule")
try:
write_mx_l3_fw_rules(file,meraki,row['id'])
except:
print("no MX firewall rule")
try:
write_vpn_settings(file,meraki,row['id'])
except:
print("no VPN")
try:
write_ssid_settings(file,meraki,row['id'])
except:
print("no SSID")
try:
write_qos(file,meraki,row['id'])
except:
print("no QoS")
try:
write_mydevices(file,meraki,row['id'])
except:
print("no devices")
file.write("except requests.exceptions.HTTPError as err:\n")
file.write("\tprint('Can not add network "+row['name']+" - it probably already exists')\n")
file.write("\n");
file.flush()

PhilipDAth
Kind of a big deal

Re: MERAKI BACKUP AND RESTORE

Thanks @Fabian1 .

AndrewE
Here to help

Re: MERAKI BACKUP AND RESTORE

I've taken @PhilipDAth's original script, added @Fabian1 portions as well which has created a Solid Organization level backup. 

 

#!/usr/bin/env python3
#
# Before running this script you need to install these two modules:
# pip install requests
# pip install meraki-sdk
# pip install -U python-dotenv
#
# Before you can use these scripts you need an API key.  Following this guide to create an API key.
# https://documentation.meraki.com/zGeneral_Administration/Other_Topics/The_Cisco_Meraki_Dashboard_API#Enable_API_access
#
# Create a ".meraki.env" file in your home directory (special note for Windows users - the filename is dot meraki dot env).
# This is used to store your sensitive information.  If you are a Windows user and you go Windows+R and type in "cmd" and
# hit return you'll have a command prompt with the current directory equal to your home directory.  From here you can
# go "notepad .meraki.env" to create the file.  A sample might look like:
# x_cisco_meraki_api_key=****************************************
# Alternatively (ie only do this if you don't do the above) you can create a .env (note dot env) file in the same
# directory as the backup script witht the same "x_cisco_meraki_api_key" field.
#
# To run a backup go:
# meraki-backup.py "org name"
# This will create a file called meraki-restore.py.  To do a restore you go:
# meraki-restore.py "org name"
# Note that the store will not overwrite existing networks.  You can either rename an existing network you want to restore
# over or edit meraki-restore.py to restore into a different [new] network.  Also you can edit meraki-restore.py to only
# restore the bits you want.
#
import os
from meraki_sdk.meraki_sdk_client import MerakiSdkClient
from meraki_sdk.exceptions.api_exception import APIException
import argparse
import json

def get_org_id(meraki,orgName):
	result = meraki.organizations.get_organizations()
	for row in result:
		if row['name'] == orgName:
			return row['id']

	raise ValueError('The organization name does not exist')

def write_restore_header(file):
	file.write("#!/usr/bin/env python3\n");
	file.write("#\n");
	file.write("# Search for \"#restored\" and edit below that to control what is restored.\n");
	file.write("#\n");
	file.write("import os\n");
	file.write("import argparse\n");
	file.write("import requests\n");
	file.write("\n");
	file.write("\n");
	file.write("from dotenv import load_dotenv\n");
	file.write("load_dotenv()\n");
	file.write("load_dotenv(dotenv_path=os.path.join(os.path.expanduser('~'),'.meraki.env'))\n");
	file.write("\n");
	file.write("parser = argparse.ArgumentParser(description='Restore a Meraki online config from an offline file.')\n");
	file.write("parser.add_argument('orgName', help='The name of a Meraki organisation')\n");
	file.write("args = parser.parse_args()\n");
	file.write("\n");
	file.write("headers = {\n");
	file.write("\t'x-cisco-meraki-api-key': os.getenv('x_cisco_meraki_api_key'),\n");
	file.write("\t'Content-Type': 'application/json'\n");
	file.write("\t}\n");
	file.write("\n");
	file.write("session = requests.Session()\n")
	file.write("\n");
	file.write("def get_org_id(orgName):\n");
	file.write("\ttry:\n")
	file.write("\t\t# https://dashboard.meraki.com/api_docs#list-the-organizations-that-the-user-has-privileges-on\n")
	file.write("\t\tgeturl = 'https://api.meraki.com/api/v0/organizations'\n")
	file.write("\t\tdashboard = session.get(geturl, headers=headers)\n")
	file.write("\t\tdashboard.raise_for_status()\n")
	file.write("\texcept requests.exceptions.HTTPError as err:\n")
	file.write("\t\tprint(err)\n")
	file.write("\n")
	file.write("\tfor row in dashboard.json():\n");
	file.write("\t\tif row['name'] == orgName:\n");
	file.write("\t\t\treturn row['id']\n");
	file.write("\traise ValueError('The organization name does not exist')\n");
	file.write("\n");
	file.write("orgid=get_org_id(args.orgName)\n");
	file.write("\n");
	file.write("\n");

def write_admins(file,meraki, orgid):
	myOrgAdmins=meraki.admins.get_organization_admins(orgid)
	file.write("# Organisation Dashboard Administrators\n")
	file.write("# https://dashboard.meraki.com/api_docs#create-a-new-dashboard-administrator\n")
	file.write("posturl = 'https://api.meraki.com/api/v0/organizations/{0}/admins'.format(str(orgid))\n")
	for row in myOrgAdmins:
		file.write("dashboard = session.post(posturl, json="+repr(row)+", headers=headers)\n")
	file.write("\n")

def write_mx_l3_fw_rules(file,meraki,networkid):
	myRules=meraki.mx_l_3_firewall.get_network_l_3_firewall_rules(networkid)[0:-1]
	file.write("\t# MX L3 Firewall Rules\n")
	file.write("\t# https://api.meraki.com/api_docs#update-the-l3-firewall-rules-of-an-mx-network\n")
	file.write("\tputurl = 'https://api.meraki.com/api/v0/networks/{0}/l3FirewallRules'.format(str(networkid))\n")
	file.write("\tdashboard = session.put(puturl, json="+str({"rules":myRules,"syslogDefaultRule":False})+", headers=headers)\n")
	file.write("\n")

def write_mx_vlans(file,meraki,networkid):
	vlanEnabled=meraki.vlans.get_network_vlans_enabled_state(networkid)

	file.write("\t# MX VLANs\n")
	file.write("\t# https://dashboard.meraki.com/api_docs#enable/disable-vlans-for-the-given-network\n")
	file.write("\tputurl = 'https://api.meraki.com/api/v0/networks/{0}/vlansEnabledState'.format(str(networkid))\n")
	file.write("\tdashboard = session.put(puturl, json="+repr(vlanEnabled)+", headers=headers)\n")

	if vlanEnabled['enabled']:
		# VLANS are enabled
		myVLANS=meraki.vlans.get_network_vlans(networkid)

		file.write("\t# https://dashboard.meraki.com/api_docs#add-a-vlan\n")
		file.write("\tposturl = 'https://api.meraki.com/api/v0/networks/{0}/vlans'.format(str(networkid))\n")
		for row in myVLANS:
			file.write("\tdashboard = session.post(posturl, json="+repr(row)+", headers=headers)\n")
		file.write("\n")
	else:
		print("warning: MX VLANs disabled - wont be able to restore IP addressing");
		
def write_mx_cellular_fw_rules(file,meraki,networkid):
	myRules=meraki.mx_cellular_firewall.get_network_cellular_firewall_rules(networkid)[0:-1]
	file.write("\t# MX cellular firewall\n")
	file.write("\t# https://dashboard.meraki.com/api_docs#mx-cellular-firewall\n")
	file.write("\tputurl = 'https://api.meraki.com/api/v0/networks/{0}/cellularFirewallRules'.format(str(networkid))\n")
	file.write("\tdashboard = session.put(puturl, json="+str({"rules":myRules,"syslogEnabled":False})+", headers=headers)\n")
	file.write("\n")

def write_mx_vpn_fw_rules(file,meraki,orgid):
	myRules=meraki.mx_vpn_firewall.get_organization_vpn_firewall_rules(orgid)[0:-1]
	file.write("# MX VPN firewall\n")
	file.write("# https://dashboard.meraki.com/api_docs#mx-vpn-firewall\n")
	file.write("puturl = 'https://api.meraki.com/api/v0/organizations/{0}/vpnFirewallRules'.format(str(orgid))\n")
	file.write("dashboard = session.put(puturl, json="+str({"rules":myRules,"syslogEnabled":True})+", headers=headers)\n")
	file.write("\n")

def write_vpn_settings(file,meraki,networkid):
	myVPN=meraki.networks.get_network_site_to_site_vpn(networkid)
	file.write("\t# Network - AutoVPN Settings\n")
	file.write("\t# https://dashboard.meraki.com/api_docs#update-the-site-to-site-vpn-settings-of-a-network\n")
	file.write("\tputurl = 'https://api.meraki.com/api/v0/networks/{0}/siteToSiteVpn'.format(str(networkid))\n")
	file.write("\tdashboard = session.put(puturl, json="+str(myVPN)+", headers=headers)\n")
	file.write("\n")

def write_snmp_settings(file,meraki,orgid):
	mySNMP=meraki.snmp_settings.get_organization_snmp(orgid)
	if 'v2CommunityString' in mySNMP:
		del mySNMP['v2CommunityString']
	if 'hostname' in mySNMP:
		del mySNMP['hostname']
	if 'port' in mySNMP:
		del mySNMP['port']
	if mySNMP['v3AuthMode'] is None:
		del mySNMP['v3AuthMode']
	if mySNMP['v3PrivMode'] is None:
		del mySNMP['v3PrivMode']

	file.write("# SNMP Settings\n")
	file.write("# https://dashboard.meraki.com/api_docs#update-the-snmp-settings-for-an-organization\n")
	file.write("puturl = 'https://api.meraki.com/api/v0/organizations/{0}/snmp'.format(str(orgid))\n")
	file.write("try:\n")
	file.write("\tdashboard = session.put(puturl, json="+str(mySNMP)+", headers=headers)\n")
	file.write("\tdashboard.raise_for_status()\n")
	file.write("except requests.exceptions.HTTPError as err:\n")
	file.write("\tprint(err)\n")
	file.write("\n")

def write_non_meraki_vpn_peers(file,meraki,orgid):
	myPeers=meraki.organizations.get_organization_third_party_vpn_peers(orgid)
	file.write("# Non Meraki VPN Peers\n")
	file.write("# https://dashboard.meraki.com/api_docs#update-the-third-party-vpn-peers-for-an-organization\n")
	file.write("puturl = 'https://api.meraki.com/api/v0/organizations/{0}/thirdPartyVPNPeers'.format(str(orgid))\n")
	file.write("try:\n")
	file.write("\tdashboard = session.put(puturl, json="+str(myPeers)+", headers=headers)\n")
	file.write("\tdashboard.raise_for_status()\n")
	file.write("except requests.exceptions.HTTPError as err:\n")
	file.write("\tprint(err)\n")
	file.write("\n")

def write_ssid_settings(file,meraki,networkid):
	mySSIDs=meraki.ssids.get_network_ssids(networkid)
	if mySSIDs is None:
		return
	file.write("\t# SSIDs\n")
	file.write("\t# https://dashboard.meraki.com/api_docs#update-the-attributes-of-an-ssid\n")
	for row in mySSIDs:
		file.write("\tputurl = 'https://api.meraki.com/api/v0/networks/{0}/ssids/"+str(row['number'])+"'.format(str(networkid))\n")
		if 'radiusServers' in row:
			print("warning: added dummy radius password for SSID "+row['name'])
			row['radiusServers'][0]['secret']='password'
		file.write("\tdashboard = session.put(puturl, json="+str(row)+", headers=headers)\n")

		myRules=meraki.mr_l_3_firewall.get_network_ssid_l_3_firewall_rules({'network_id':networkid, 'number':row['number']})[0:-2]
		file.write("\t# MR L3 firewall\n")
		file.write("\t# https://dashboard.meraki.com/api_docs#update-the-l3-firewall-rules-of-an-ssid-on-an-mr-network\n")
		file.write("\tputurl = 'https://api.meraki.com/api/v0/networks/{0}/ssids/"+str(row['number'])+"/l3FirewallRules'.format(str(networkid))\n")
		file.write("\tdashboard = session.put(puturl, json="+str({"rules":myRules,"allowLanAccess":True})+", headers=headers)\n")
		file.write("\n")

def write_mydevices(file,meraki,networkid):
        mydevice=meraki.devices.get_network_devices(networkid)
        if mydevice is None:
                return
        file.write("\t# Devices\n")
        file.write("\t# https://developer.cisco.com/meraki/api/#/rest/api-endpoints/devices/update-network-device\n")
        for row in mydevice:
                file.write("\tputurl = 'https://api.meraki.com/api/v0/networks/{0}}/devices/claim.format(str(networkid))\n")
                file.write("\tdashboard = session.put(puturl, json="+str(row['serial'])+", headers=headers)\n")
                file.write("\tputurl = 'https://api.meraki.com/api/v0/networks/{0}/devices/"+str(row['serial'])+"'.format(str(networkid))\n")
                file.write("\tdashboard = session.put(puturl, json="+str(row)+", headers=headers)\n")
                if 'switchProfileId' in row:
                        switchports=meraki.switch_ports.get_device_switch_ports(row['serial'])
        for port in switchports:
                file.write("\tputurl = 'https://api.meraki.com/api/v0/devices/"+str(row['serial'])+"'/switchPorts/"+str(port['number'])+"'.f...")
                file.write("\tdashboard = session.put(puturl, json="+str(port)+", headers=headers)\n")
	

from dotenv import load_dotenv
load_dotenv()
load_dotenv(dotenv_path=os.path.join(os.path.expanduser("~"),".meraki.env"))

parser = argparse.ArgumentParser(description='Backup a Meraki config to an offline file.')
parser.add_argument('orgName', help='The name of a Meraki organisation')
args = parser.parse_args()

meraki = MerakiSdkClient(os.getenv("x_cisco_meraki_api_key"))
orgid = get_org_id(meraki,args.orgName)

with open('meraki-restore.py', 'w') as file:
	write_restore_header(file);

	file.write("# Edit script below this line to control what is #restored.\n");
	file.write("\n");

	file.flush()

	write_admins(file,meraki, orgid);
	write_mx_vpn_fw_rules(file,meraki,orgid)
	write_snmp_settings(file,meraki,orgid)
	write_non_meraki_vpn_peers(file,meraki,orgid)

	file.flush()
		
	myNetworks = meraki.networks.get_organization_networks({"organization_id": orgid})
	for row in myNetworks:
                if row['type'] == 'systems manager':
                        continue
                if row['tags'] is None:
                        del row['tags']

                status="Processing network "+row['name']
                print(status)
                file.write("# Add Network: "+row['name']+"\n")
                file.write("print('"+status+"')\n")

                file.write("try:\n")
                file.write("\t# https://dashboard.meraki.com/api_docs#create-a-network\n")
                file.write("\tposturl = 'https://api.meraki.com/api/v0/organizations/{0}/networks'.format(str(orgid))\n")
                file.write("\tdashboard = session.post(posturl, json="+repr(row)+", headers=headers)\n")
                file.write("\tdashboard.raise_for_status()\n")

                file.write("\tnetworkid=dashboard.json()['id']\n")
                file.write("\n")

                try:
                        write_mx_vlans(file,meraki, row['id'])
                except:
                        print("no mx VLAN")
                try:
                        write_mx_cellular_fw_rules(file,meraki,row['id'])
                except:
                        print("no mobile firewall rule")
                try:
                        write_mx_l3_fw_rules(file,meraki,row['id'])
                except:
                        print("no MX firewall rule")
                try:
                        write_vpn_settings(file,meraki,row['id'])
                except:
                        print("no VPN")
                try:
                        write_ssid_settings(file,meraki,row['id'])
                except:
                        print("no SSID")
                try:
                        write_qos(file,meraki,row['id'])
                except:
                        print("no QoS")
                try:
                        write_mydevices(file,meraki,row['id'])
                except:
                        print("no devices")

                file.write("except requests.exceptions.HTTPError as err:\n")
                file.write("\tprint('Can not add network "+row['name']+" - it probably already exists')\n")
                file.write("\n");
                file.flush()

 

PhilipDAth
Kind of a big deal

Re: MERAKI BACKUP AND RESTORE

I've added the try/except blocks now @Fabian1 .  So that should stop the script aborting on more of the corner cases.

Markibr
New here

Re: MERAKI BACKUP AND RESTORE

Hi @PhilipDAth, trying to run your python script to backup a MS225 and get the follwoing error below, any ideas you can assist me with?

 

LT-APL0062603:~ markibrahim$ python3 meraki-backup.py "Healthcare Network of SW FL"

Traceback (most recent call last):

  File "meraki-backup.py", line 27, in <module>

    from meraki_sdk.meraki_sdk_client import MerakiSdkClient

ModuleNotFoundError: No module named 'meraki_sdk'

BrechtSchamp
Kind of a big deal

Re: MERAKI BACKUP AND RESTORE

Was your pip install meraki-sdk command succesful?

Markibr
New here

Re: MERAKI BACKUP AND RESTORE

@BrechtSchamp  once I installed "pip install meraki-sdk==1.5.0" it worked.

ebakke
New here

Re: MERAKI BACKUP AND RESTORE

Hi!

 

I running Python 3.7.3 and have installed meraki sdk 1.5.

 

When I try to run the backup script I get:

Traceback (most recent call last):
File "meraki-backup.py", line 27, in <module>
from meraki_sdk.meraki_sdk_client import MerakiSdkClient
ModuleNotFoundError: No module named 'meraki_sdk'

 

Any Idea? 

PhilipDAth
Kind of a big deal

Re: MERAKI BACKUP AND RESTORE

It means the Meraki SDK is not install.  So something must have gone wrong during that install process.

ebakke
New here

Re: MERAKI BACKUP AND RESTORE

installed with success. i have also did pip uninstall and install again
BrechtSchamp
Kind of a big deal

Re: MERAKI BACKUP AND RESTORE

What's the output if you execute the following function from within python:

 help("modules")

 

moacosta
Conversationalist

Re: MERAKI BACKUP AND RESTORE

taking some of code mentioned in the thread, we did a GUI to schedule the backup file and execute the backup file at the date and time define

 

you can review it here 

 

 

https://github.com/gve-sw/ConfigurationBackupPlanner/blob/master/README.md

 

 

Alex07
Here to help

Re: MERAKI BACKUP AND RESTORE

Hi Phillip,

 

I'm facing this issue when starting the script:

 

----------------python3 meraki-backup.py "My Network"

Traceback (most recent call last):
File "meraki-backup.py", line 209, in <module>
orgid=get_org_id(meraki,args.orgName)
File "meraki-backup.py", line 33, in get_org_id
result = meraki.organizations.get_organizations()
File "/usr/local/lib/python3.6/dist-packages/meraki_sdk/controllers/organizations_controller.py", line 50, in get_organizations
self.validate_response(_context)
File "/usr/local/lib/python3.6/dist-packages/meraki_sdk/controllers/base_controller.py", line 94, in validate_response
raise APIException('HTTP response not OK.', context)
meraki_sdk.exceptions.api_exception.APIException: HTTP response not OK.
PhilipDAth
Kind of a big deal

Re: MERAKI BACKUP AND RESTORE

HI @Alex07 .

 

I'm not sure what is causing that.  Perhaps try update your modules:

pip install -U requests
pip install -U meraki-sdk
pip install -U python-dotenv

 

Double-check your API key is correct.

Guruprakash_M
Comes here often

Re: MERAKI BACKUP AND RESTORE

Hi,

 

Would you help us to know the native Meraki config rollback options in the dashboard? For instance, we have performed certain misconfiguration and we wanted to rollback to previous last known good configuration on date & time wise

 

Bruce
Kind of a big deal

Re: MERAKI BACKUP AND RESTORE

There is no native rollback in the Meraki Dashboard. The closest option you have is to work backwards through the Change Log (Organization -> Change Log) to undo whatever changes were made.

Guruprakash_M
Comes here often

Re: MERAKI BACKUP AND RESTORE

Thanks for the swift reply.

We tried this option which doesn't help for all the situations. Recently one of our non-Meraki IPSec VPN config were disturbed wherein we couldn't make use this this feature as you suggested.

 

The change log doesn't have required parameters like Peer IP address and other non confidential things to refer back and re-configure...  Can you assist us how to overcome such situation

Masar
New here

Re: MERAKI BACKUP AND RESTORE

Hi Philip, 

I do the steps that you recommended in your link, but if I run the meraki.py xxxx"xxx", does nothing and gives no error also. Can you help please with this? 

PhilipDAth
Kind of a big deal

Re: MERAKI BACKUP AND RESTORE

>I do the steps that you recommended in your link, but if I run the meraki.py xxxx"xxx", does nothing and gives no error also

 

It is unusual to get no output.  It sounds like fundamental is wrong in your environment.  Are you sure you have Python 3.x installed?  Are you able to run other Python scripts ok?

cwal21
Here to help

Re: MERAKI BACKUP AND RESTORE

Thanks for the writeup and scripts provided @PhilipDAth this really helps fill a void that should already be present via a "backup config" button somewhere on the dashboard from Meraki out of the box. As user friendly the Meraki line is, one would think this simple option would be made available to satisfy backup config requests from auditors and such for financial institutions and others.

 

I completed the steps and successfully generated the restore file. The only warnings i received referenced MX VLANs disabled - wont be able to restore IP addressing. Does this mean if a restore is actually done, no ip addresses will be restored? Or can you elaborate on the warning message?

 

Also, to avoid overwriting networks, is all that is required to restore to a tentative backup network changing the name of the network referenced (in my case 4 times) to something else and a new network name will appear in the dashboard?

 

Thanks again!

PhilipDAth
Kind of a big deal

Re: MERAKI BACKUP AND RESTORE

>The only warnings i received referenced MX VLANs disabled - wont be able to restore IP addressing. Does this mean if a restore is actually done, no ip addresses will be restored? 

 

Correct.  If you want to be able to restore interface IP addresses then you'll need to enable VLANs on the MX (even if you only have a single VLAN).

 

>Also, to avoid overwriting networks, is all that is required to restore to a tentative backup network changing the name of the network referenced

 

Correct.  That is what I do.  The other option is to delete the bits you don't want to be restored.

Borisorism
Meraki Employee

Re: MERAKI BACKUP AND RESTORE

@PhilipDAth - am I right to think that your script does not save switch configuration?
Great work on this btw.

PhilipDAth
Kind of a big deal

Re: MERAKI BACKUP AND RESTORE

Correct @Borisorism .  At the time I wrote it, there wasn't an API available to get switch configuration.

 

One day I'll re-write it to use the V1 API and will be able to add more stuff in.

Welcome to the Meraki Community!
To start contributing, simply sign in with your Cisco account. If you don't yet have a Cisco account, you can sign up.