Morning all.
 
So, at a conference where we needed to push an update to an app immediately because of bugs, etc.
 
Whilst you can go into a device's info page and push, and also do this from other pages, we wanted to ensure that we only did this for devices where the app version DIDN'T match a particular version, to avoid disruption to other devices
 
You'll need the following libraries installed:
- Meraki
 - logging, sys, getopt, csv
 - pandas
 - json 
 
 
You'll call it with:
 
python3 PushAppByAppIDReadFromCSV.py -k <YOURAPIKEY> -n <YOURMERAKINETWORKID>
 
It requires a download of the CSV for the app in Question. This is from Systems Manager > Software. Click on the required app, then Export to CSV :
 
It requires this to be called : 
devices_with_software.csv

 
It also requires your network ID: Go to Systems Manager > General and scroll all the way to the bottom of the page
 
Lastly, it requires the SM app ID: Go to Systems Manager > Apps and click on the required app. In the URL will be the SM app ID
 
We use PANDAS for three reasons:
- It's really good for importing multi columned CSVs
 - It's fast!
 - It's really easy to import a list of dictionaries and working with it, which is what the response is from the GetNetworkSMDevices API call
 
 
 
import meraki
import logging, sys, getopt, csv
import pandas as pd
import json
def main(argv):
    # static args:
    # Most recent version number of the app
    search_value = '5.0.0'
    # app ID in SM
    app_ids = ['57814960XXXXXXXXXXXXXXXXXXX']
    
    # init vars
    arg_apikey = ''
    arg_network_id = ''
    # read ARGs
    try:
        opts, args = getopt.getopt(argv, 'k:n:')
    except getopt.GetOptError:
        printhelp(argv)
        sys.exit(2)
    for opt, arg in opts:
        if opt == '-k':
            arg_apikey = arg
        elif opt == '-n':
            arg_network_id = arg
    if arg_apikey == '' or arg_network_id == '':
        printhelp(argv)
        sys.exit(2)
    # Read the CSV
    df = pd.read_csv('devices_with_software.csv')
    # Filter rows where 'column_name' equals the search_value
    # and create a new data frame
    filtered_df = df[df['version'] != search_value]
    client = meraki.DashboardAPI(api_key=arg_apikey)
    # get all devices in the sm network
    deviceList = client.sm.getNetworkSmDevices(networkId=arg_network_id)
    # MUCH easier to work with DICTs in a LIST in Pandas, so let's import the 
    deviceListDF = pd.DataFrame(deviceList)
    # iterate through filtered_df to get serial number
    for index, row in filtered_df.iterrows():
        serialNumber = row["serialNumber"]
        # now search through deviceListDF and get SM device ID
        try:
            # we'll have a try, just in case the device weirdly doesn't exist
            deviceIDList = deviceListDF.loc[deviceListDF['serialNumber'] == serialNumber, 'id'].values
            deviceID = str(deviceIDList[0])
            print("Device ID: ", deviceID, " serial number: ", serialNumber)
            try:
                response = client.sm.installNetworkSmDeviceApps(
                    arg_network_id, deviceID, app_ids,
                    force=True
                )
            except meraki.APIError as e:
                print(e.message)
                sys.exit(2)
        except Exception as e:
            print(e)
def printhelp():
    # prints help information
    print('This is a script that updates a given app where the app is not the most recent version.')
    print("You'll need to export the CSV from:")
    print("* Systems Manager > Software")
    print("* Click on the app in question")
    print("* Export to CSV on the right")
    print(" ")
    print(" It needs:")
    print("serial, name,,NetworkName")
    print('')
    print('Mandatory arguments:')
    print(' -k <api key>        : Your Meraki Dashboard API key')
    print(' -n number           : Your Meraki Network ID ')
if __name__ == '__main__':
    main(sys.argv[1:])
 
Good luck!