├── .gitignore ├── requirements.txt ├── README.md └── jss-advanced_search-to-google_sheets.py /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | include 3 | pip* 4 | lib 5 | .Python 6 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | cffi==1.2.1 2 | cryptography==1.0.2 3 | enum34==1.0.4 4 | google-api-python-client==1.4.2 5 | gspread==0.2.5 6 | httplib2==0.9.2 7 | idna==2.0 8 | ipaddress==1.0.14 9 | oauth2client==1.5.1 10 | pyasn1==0.1.9 11 | pyasn1-modules==0.0.8 12 | pycparser==2.14 13 | pyOpenSSL==0.15.1 14 | requests==2.8.0 15 | rsa==3.2 16 | simplejson==3.8.0 17 | six==1.10.0 18 | uritemplate==0.6 19 | wheel==0.24.0 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JSS-Advanced-Search-to-Google-Sheets 2 | Take any Computer/Mobile Advanced Search in the JSS and publish it to Google Sheets
3 | Great for sharing reports with people that don't need access to your JSS
4 | Run it on a schedule to deliver reports on time, every time!
5 | 6 | ## Features 7 | 10 | 11 | ## Requirements: 12 | A little bit of time on your part - It's not too bad though, just a lot of things to setup the first time.
13 | Google Apps for Domain/Education
14 | Access to create an account, turn on developer console for an org, and Manage API Client Access
15 | A JAMF Software Server (Tested against version 9.8)
16 | An account with API read permissions to Mobile Device/Computer Advanced Searches and Mobiles Devices/Computers
17 | 18 | # How to get this all setup 19 | ## Create a new Google Apps User
20 | Go to admin.google.com and sign in with an admin account
21 | Create an OU called Developers or similar
22 | Create a new user for the project and put them in that organization
23 | 24 | ## Give permissions to the organization 25 | Go to Apps
26 | Go to Additional Services
27 | Go to Google Developers Console
28 | Turn on for some organizations and select the Developers organization
29 | 30 | ## Create the developer project so you can get the OAuth key 31 | Go to the Google Developers Console: https://console.developers.google.com/
32 | Sign in with your new Google Apps Developer account
33 | Go to Select a Project --> Create Project
34 | Give it a meaningful name
35 | Read and agree to the terms and click create
36 | Go to API and Auth, Click on APIs
37 | Enable Google Drive API
38 | Click on Credentials, Click Add Credentials
39 | Click on Service Account
40 | Select p12 as the key type
41 | Click create
42 | Click close
43 | You now have a service account, click on the email address
44 | Copy the Client ID and email address to a safe location
45 | The p12 key should download to your computer
46 | 47 | ## Authorize the API account 48 | Go back to admin.google.com
49 | Click on Security
50 | Check Enable API Access
51 | Scroll down to Advanced settings and click Manage API client Access
52 | Paste the Client ID from the credentials page into Client Name
53 | Paste https://spreadsheets.google.com/feeds/ into One or More API Scopes
54 | Click Authorize
55 | You should now see an entry for your Client ID and Spreadsheets (Read/Write)
56 | 57 | ## Setup a Google Sheet 58 | Sign into drive.google.com as the api user or yourself and create a new sheet.
59 | Share that sheet with the API user if you used another account
60 | Give the Workbook a name
61 | Give the Sheet a name
62 | Get the spreadsheet key (Part of the URL that is before /edit )
63 | Looks like this: 1pasdfsBr_8a3anlLDIdiSLENlsdnOK9s7bJqhdGow
64 | 65 | ## Create advanced search 66 | Create an advanced search as desired, find the id (found in the URL)
67 | 68 | ## Get the script 69 | Download this project
70 | Create a folder next to the script called data
71 | Create a folder inside data called key
72 | Put your .p12 key into the key folder and call it sheets.p12
73 | Open your favorite terminal
74 | cd into project folder 75 | Run: ```sudo pip install -r requirements.txt``` 76 | 77 | ## Lets set the variables 78 | Set workbook key
79 | Set worksheet name
80 | Set google_api_user (This is the long email address we generated in the developer console)
81 | Set google_user (This is the developer email address - what you logged into Sheets and Developer console with)
82 | Set jss settings, host, username, password, etc
83 | Set the as_id (Advanced Search ID)
84 | Set the as_type by uncommenting
85 | 86 | ## Run it! 87 | ```python jss-advanced_search-to-google_sheets.py``` 88 | 89 | ## TIPS 90 | Cron it! or launchd it!
91 | Format your spreadsheet - updates will not modify format. Hidden columns will remain hidden. Bold cells will remain bold.
92 | Conditional Formatting can be good for dealing with unknown table lengths
93 | 94 | ## Known Limitations 95 | Can have more than 26 columns - should be easy to accomodate, just a day two thing...
96 | I think you will have to have a Google Apps for Domain/Education account. This will not work with a personal account that I am aware of.
97 | Right now there are a few columns that come down whether or not they are selected. This could be handled by the script.
98 | 99 | ## Warranty 100 | I offer no warranty for this script and am not liable if I blow up your JSS or GAFE environment :)
101 | 102 | ## Next steps: 103 | Make it so it can loop over multiple sheets and reports so only one script is needed to handle multiple reports.
104 | Add more than 26 columns
105 | Ability to hide UDID, name, and id fields unless specified by the report.
106 | Logging
107 | 108 | ## Suggestion 109 | Are welcome!
110 | -------------------------------------------------------------------------------- /jss-advanced_search-to-google_sheets.py: -------------------------------------------------------------------------------- 1 | # JSS Advanced Search to Google Sheet 2 | # Authored by Brad Schmidt on 10/8/2015 3 | # Requires a Google developer account, project, and key. See DOCS for more information on how to obtain 4 | # the necessary OAuth information for Google Sheets API to function 5 | # The OAuth key will be stored in ./data/key/sheets.p12 6 | # The credentials will be stored in ./data/sheets.dat 7 | 8 | # Please fill out the variables below 9 | 10 | # Variables 11 | workbook_key = "" 12 | worksheet_name = "" 13 | 14 | # Oauth 15 | google_api_user = '' 16 | google_user = '' 17 | 18 | # JSS Authentication 19 | jss_host = "" # Include http:// or https:// leave off port number Example: https://your.jss.com 20 | jss_port = "8443" # Port number 21 | jss_path = "" # Context -- if you need it: Enter it with a forward slash. Example: If your JSS is https://your.jss.com:8443/dev you would enter /dev 22 | jss_username = "" # Setup a user with API rights to read Advanced Computer and Mobile Reports as well as Computers and Mobile Devices 23 | jss_password = "" # Password 24 | 25 | # Advanced search ID and Type 26 | as_id = "" 27 | 28 | # Advanced search ID Type -- Uncomment one 29 | #as_type = "Computer" 30 | #as_type = "Mobile" 31 | 32 | ################################################################ 33 | ######### You should not have to modify below this line ######## 34 | ################################################################ 35 | 36 | ################################################################ 37 | ##################---Importing Libraries---##################### 38 | ################################################################ 39 | import sys 40 | import httplib2 41 | import string 42 | import googleapiclient 43 | import oauth2client 44 | from apiclient.discovery import build 45 | from oauth2client.file import Storage 46 | from oauth2client.client import SignedJwtAssertionCredentials 47 | from oauth2client.client import OAuth2WebServerFlow 48 | 49 | # OS path to pickup files,etc 50 | import os.path 51 | SITE_ROOT = os.path.dirname(os.path.realpath(__file__)) 52 | # Handles json parsing 53 | import json 54 | 55 | # Allows us to work with the Google Sheet 56 | import gspread 57 | 58 | # requests makes the JSS connection 59 | import requests 60 | 61 | # Mobile or Computer Search - set values 62 | def as_type_f(as_type): 63 | if as_type == "Computer": 64 | return "advancedcomputersearches","advanced_computer_search","computers" 65 | if as_type == "Mobile": 66 | return "advancedmobiledevicesearches","advanced_mobile_device_search","mobile_devices" 67 | else: 68 | print 'Failed to set as_type properly.\rPlease uncomment as_type = "Computer" or as_type = "Mobile"' 69 | 70 | # Get the report data 71 | def getReportData(as_path,as_key,as_rows): 72 | # Make the request of the JSS API 73 | r = requests.get(jss_host + ':' + jss_port + jss_path + '/JSSResource/' + as_path + '/id/' + as_id, headers={'Accept': 'application/json'}, auth=(jss_username,jss_password)) 74 | # logging.info("Response: %s" %r.text) 75 | 76 | # Get the device data we need 77 | report_data = r.json()[as_key] 78 | 79 | # Get the header values 80 | for c in report_data[as_rows]: 81 | columns = c.keys() 82 | 83 | # Return the data and the headings 84 | return report_data[as_rows],columns 85 | 86 | # This function authenticates the Sheets session 87 | def oauthSheets(): 88 | ####--logging.info("Authenticating using OAuth for Google Sheets") 89 | f = file('%s/%s' % (SITE_ROOT,'data/key/sheets.p12'), 'rb') 90 | key = f.read() 91 | f.close() 92 | http = httplib2.Http() 93 | storage = Storage(SITE_ROOT + '/data/sheets.dat') 94 | credentials = storage.get() 95 | if credentials is None or credentials.invalid: 96 | credentials = SignedJwtAssertionCredentials(google_api_user, key, scope='https://spreadsheets.google.com/feeds/',sub=google_user) 97 | storage.put(credentials) 98 | else: 99 | credentials.refresh(http) 100 | http = httplib2.Http() 101 | http = credentials.authorize(http) 102 | gc = gspread.authorize(credentials) 103 | return gc 104 | 105 | def publish(report,columns): 106 | ####--logging.info("Checking to see if the user has filled out the UA") 107 | # Oauth Sheet 108 | 109 | gc = oauthSheets() 110 | # Select the sheet 111 | wks = gc.open_by_key(workbook_key) 112 | worksheet = wks.worksheet(worksheet_name) 113 | 114 | # Clear out existing data from the sheet 115 | # Get length of spreadsheet 116 | rows = worksheet.col_values(1) 117 | number_of_rows = len(rows) 118 | 119 | # Get the width of the spreadsheet 120 | cols = worksheet.row_values(1) 121 | number_of_columns = len(cols) 122 | 123 | # If the sheet is blank skip over the clear portion. A range error will occur if it is blank 124 | if number_of_columns != 0 and number_of_rows != 0: 125 | 126 | # Now that we have the coordinates let's set the range 127 | cell_list = worksheet.range('A1:%s%s' % (string.uppercase[number_of_columns],number_of_rows)) 128 | 129 | # Set each cell to "" 130 | for cell in cell_list: 131 | cell.value = "" 132 | 133 | # Update the cells all at once 134 | worksheet.update_cells(cell_list) 135 | 136 | # Setup the Header row 137 | # Get the number of columns provided by the Advanced Search 138 | number_of_columns = len(columns) - 1 139 | 140 | # Set the cell range 141 | cell_list = worksheet.range('A1:%s1' % string.uppercase[number_of_columns]) 142 | 143 | # Create a list of column header values 144 | header_data = [] 145 | for header in columns: 146 | header_data.append(header) 147 | 148 | # Iterates through each value in the list and each cell 149 | for heading, cell in zip(header_data,cell_list): 150 | cell.value = heading 151 | 152 | # Update the sheet with column headers 153 | worksheet.update_cells(cell_list) 154 | 155 | # Prepare the data from the Advanced Search 156 | search_data = [] 157 | for line in report: 158 | for header in columns: 159 | cell = line.get(header) 160 | search_data.append(cell) 161 | 162 | # Let's see how many rows are in the report 163 | rows = len(report) 164 | 165 | # Select a range 166 | cell_list = worksheet.range('A2:%s%s' % (string.uppercase[number_of_columns],rows + 1)) 167 | 168 | # Set the cell value while iterting through values and cells 169 | for value, cell in zip(search_data,cell_list): 170 | cell.value = value 171 | 172 | # Update the spreadsheet with the report data 173 | worksheet.update_cells(cell_list) 174 | 175 | # Is it Mobile or Computer, return approprtiate values for parsing the report 176 | # Let's make sure the value was uncommented 177 | try: 178 | as_type 179 | as_path,as_key,as_rows = as_type_f(as_type) 180 | except NameError: 181 | print 'Failed to set as_type properly.\rPlease uncomment as_type = "Computer" or as_type = "Mobile"' 182 | sys.exit(1) 183 | 184 | # Get the data from the advanced search 185 | report,columns = getReportData(as_path,as_key,as_rows) 186 | 187 | # Publish to Google Sheet 188 | publish(report,columns) 189 | --------------------------------------------------------------------------------