REST API

### URI

Old API URI example: https://host/api/v1/mam/apps/public/{applicationid}/addsmartgroup/{smartgroupid}

New API URI example: https://host/api/mam/apps/public/{applicationid}/smartgroup/{smartgroupid}

Attached Documents

REST API SDK Guide Deprecated REST API update Main REST API Guide VMware AirWatch Peripheral API Guide VMware AirWatch API Programming Quick Start Guide

Articles in section

Preparation

You need a user account to run scripts in AirWatch REST API. Create a local account, take its’ credentials: domain\login:password, and go encode it at https://www.base64encode.org/

For example, 'lab\restguy:P@ssw0rd' ==> 'bGFiXHJlc3RndXk6UEBzc3cwcmQ='._

Turn API access ON for this account in the AirWatch console System -> Advanced -> API -> REST, we also should get an API Key for it.

Warning
  • AirWatch requires all requests to be made over SSL.

  • AirWatch limits the number of API requests that can be made during certain time periods. Rate limiting is done per Organization Group (OG) based on the API key used in the request.

    **Server Throttling** - The limit imposed on an Organization Group for a 1 minute interval.  
    **Daily Quota** - The limit imposed on an Organization Groupvfor a 24-hour interval.
    

Local REST API Help

REST API Help is built into the AirWatch API Server host, go to link: https:///api/help/

Examples of using REST API

Let’s take some device from those enrolled in AirWatch and check out the apps list on it.

import requests
consoleURL = 'https://airwatch.example.com'
b64EncodedAuth = 'bGFiXHJlc3RndXk6UEBzc3cwcmQ='
API_Key = 'S390lvIVeFRBSO4InP73MFG3YSFDNKL+BXKW7Wv5Wwo='
basic_headers={"Authorization": "Basic " + b64EncodedAuth, "aw-tenant-code": APItenantCode,"Accept": "application/json"}
 
appsTest = requests.get(consoleURL + "/api/mam/apps/internal/46", headers=basic_headers)
print(appsTest.json())

46 is the device ID inside AirWatch. How do you find the ID of a device? - simplest way to do this is open AirWatch console, device list page, and hover your mouse over any device name: you will see the ID in the browser URL status bar. Now, let’s see where the device has been for the last 2 days:

gps2days = requests.get(consoleURL + "/api/mdm/devices/46/gps?dayrange=2", headers=basic_headers)
print(gps2days.json())

If there are any GPS points recorded for the device, we will get a list of them here. How can we use this? For example, customers say they hate Bing Maps embedded in AirWatch console. So we can build a little portal for searching devices, embed Google Maps into it and use the coordinates list to draw points.

One step deeper: suppose we need to show a customer how GPS coordinates are being collected, but the device enrolled is fresh, did not catch any sattelites yet or has problems with GPS module. Since GPS data is actually stored and taken from the SQL database, we can insert some coordinates there manually and see them right away on the Bing map in the console. See article on inserting false GPS history for a device in SQL section

Last example in this article I got from a cool client. They give out corporate iPhones to their employees, and all of those devices are supposed to be supervized. Only supervized devices are to be enrolled in AirWatch and get corp data, no personal gadgets coming through! AirWatch does have a tag for Apple device status (is it Supervized/DEP/Education?), but uses it only for reporting - there is no Compliance rule or filtering around this currently (a ticket in Jira on the topic is promised to be closed in AirWatch 9.6+). So the client gets a list of all enrolled devices and filters them manually:

import json
devicelist = requests.get(consoleURL + '/api/mdm/devices/search', headers=basic_headers)
for i in devicelist.json()["Devices"]:
    if i["Platform"] == "Apple" and i["IsSupervised"] == False and i["EnrollmentStatus"] == "Enrolled":
        # Tag the toxic devices, or enterprise wipe them

By running this script every half-hour, all unsupervised devices are Enterprise Wiped shortly after they are enrolled.

Subsections of REST API

REST API Example on Python

Several typical functions of working with REST API:

  • Searching for devices in a tenant
  • Filtering devices by OS (Win/Linux)
  • Getting a list of tags for the tenant
  • Scanning a file with list of Notebook/Smartphone/Tablet serial keys, then checking if enrolled devices have such serials, and writing the result in a file
Tip

Default tags have ID 1-8. User-created tags have ID=10000+

  • Assigning devices to a chosen tag

Code is for Python version 3.7+, and is bundled into a dataclass as methods, with error-catching and logging support.

Link to code on GitHub.

'''Version 0.3
Created by Alexei Rybalko aka Aagern.
22.05.2021'''
 
import requests, json, base64, logging, sys
from dataclasses import dataclass
from pprint import pprint
 
logging.basicConfig(filename="DeviceControl.log", level=logging.DEBUG,
                    format="%(asctime)s:%(levelname)s:%(message)s")
 
@dataclass
class DeviceControl:
    API_URL: str = 'https://mdm.example.local'
    API_Key: str = 'oxGI6OORljw/Qsql1OFBycjHvzQzEXVXa/tyEcCfIPI='
    API_User: str = 'defaultDomain\\api_user:VMware1!'
    Serials_File: str = 'S.csv'
    Output_File: str = 'DeviceSerials.csv'   
     
    API_User_B64 = base64.b64encode(API_User.encode('utf-8'))  # convert to Base64
    API_User_UTF8 = API_User_B64.decode('utf-8')
    Platforms = {'Windows':12,'Linux':21}
    Header = {"Authorization": "Basic " + API_User_UTF8, "aw-tenant-code": API_Key, "Accept": "application/json"}
    Devices = {}
    Serials = []
     
    def getMDM(self,request):
        """Get data from MDM Server. Input: API request text. Output: response data in JSON."""
        try:
            dataMDM = requests.get(self.API_URL + request, headers=self.Header)
            dataMDM.raise_for_status()
            data = dict(dataMDM.json())
        except requests.exceptions.RequestException as e:
            logging.error(f'Get request failed with {e}')
            sys.exit(1)
        logging.debug(f'Data received from {self.API_URL}')
        return data
         
    def postMDM(self,request,data):
        """Post data to MDM Server. Input: API request text. Output: HTTP response status and details."""
        try:
            responseMDM = requests.post(self.API_URL + request, headers=self.Header, json=data) # Important to do json= here, rather than prepare data with json.dumps()
        except requests.exceptions.RequestException as e:
            logging.error(f'Post request failed with {e}')
            sys.exit(1)
        logging.debug(f'Data sent to {self.API_URL}')       
        return responseMDM
     
    def scanSerialsFile(self,file=Serials_File):
        """Input file with device serials. Output list of serials from file"""
        try:
            with open(file,encoding='utf-8') as f:
                for serial_line in f:
                    self.Serials.append(serial_line)
                    Serials[0] = Serials[0][1:] # Omitting special symbol at file start
                logging.debug(f'Serials file {file} processed.')
        except OSError as e:
            logging.error(f'File opening error with {e}')
            print('File not opened. Serials list empty')
            self.Serials = []
        return self.Serials
             
    def filterDevices(self,platform='Windows'):
        """
        Method filters out Devices by Platform.
        Inputs: Platform name.
        Outputs: File with serials, Dictionary of Device:[Serial,ID]
        """
        PlatformID = self.Platforms[platform]
        dataDict = self.getMDM('/api/mdm/devices/search')
        for device in dataDict['Devices']:
            if device['PlatformId']['Id']['Value'] == PlatformID:
                self.Devices[device['DeviceFriendlyName']] = [device['SerialNumber'] if device['SerialNumber'] else 'Not set',device['Id']['Value']]
        try:
            with open(self.Output_File,mode="wt",encoding='utf-8') as output_file:
                for device,serial in self.Devices.items():
                    print(f"{device},{serial},",file=output_file)
                    if serial in Serials: print("Registered\n",file=output_file)
                    else: print("NOT Registered\n",file=output_file)
                logging.debug(f'Output file {self.Output_File} written.')
        except OSError as e:
            logging.error(f'File opening error with {e}')
            print('File not written.')
            sys.exit(1)
        return self.Devices
     
    def getMDMTags(self,tenant='570'):
        """Get all tags in tenant. Input: tenant ID. Output: list of tags and their IDs."""
        tagsDict = self.getMDM(f'/api/system/groups/{tenant}/tags')
        for tag in tagsDict['Tags']:
            print(f"Name={tag['TagName']}\t\tID={tag['Id']}")
             
    def setTagDevices(self,tagID='10000',DeviceIDs=['3']):
        """Method assigns devices to tag. Input: tag id, devices ID list. Output: total  of assigned devices"""
        FuncDeviceIDs = { "BulkValues":{"Value":DeviceIDs}}
        response = self.postMDM(f'/api/mdm/tags/{tagID}/adddevices',FuncDeviceIDs)
        print(f'Status code: {response.status_code}, \n {response.text}')
 
# Examples of usage
 
Device = DeviceControl()
data = Device.filterDevices()
Device.getMDMTags()
Device.setTagDevices()