Create Script for Collecting All Deployed Software on All Devices

Use the script Aternity supplies to collect data about all deployed software in your organization. It queries all distinct deployed applications, data for each application, and writes the queried data to the CSV file.

Tip

You can also see this data via the REST API called INSTALLED_SOFTWARE. Learn more. Using the script is recommended in large deployments when the amount of data might be too long for REST API tables.

Before you begin

  • Make sure you have Python 3 or later

  • Make sure you have Requests and Validators Python Packages (Run each command separately in the Command Line: pip install requests and pip install validators)

  • Make a note of Aternity REST API base URL (for example, https://my-demo-odata.aternity.com .

  • Make sure you have Aternity REST API user name and password. You can find this by selecting User icon > REST API Access. SSO users must generate (once) and use a special password, as Aternity's REST API does not authenticate with your enterprise's identity provider.

Procedure

  1. Step 1 Copy the sample code in the bottom of the article and save it as odata_installed_software_script.py.
  2. Step 2 Edit odata_installed_software_script.py to set the following fields:
    Field Description
    BASE_URl =

    Enter Aternity REST API base URL. Put values within a pair of single quotation marks (for example, 'https://my-demo-odata.aternity.com').

    REST_API_USERNAME =

    Enter Aternity REST API username. Put values within a pair of single quotation marks.

    REST_API_PASSWORD =

    Enter Aternity REST API password. Put values within a pair of single quotation marks.

    CSV_FILE_PATH =

    (Optional) Enter the full path for the output CSV file. Put values within a pair of single quotation marks.

    If you do not specify the full path for the output CSV file, it will store the data in the install_software.csv file in the directory from which you run the script.

    COLUMNS_TO_SELECT =

    (Optional) Remove the columns you do not want to include in your query.

  3. Step 3 Save the changes.
  4. Step 4 In the Command Line run python odata_installed_software_script.py
    Tip

    When running, the script is writing to the screen for each application it queries: the Number of applications left: N and Downloading application Application_Name

    To tell the person running the script that it finished successfully, we display a success message to the screen. If there is an error, it will be printed to the user and a stack trace.

    Note

    This may take a very long time to run the script depending on the number of deployed applications and the number of monitored devices.

    import csv
    import traceback
    import urllib.parse
    from typing import Callable, List, Dict
    
    import requests  # requires pip install requests
    import validators  # requires pip install validators
    
    # Constants
    NEXT_PAGE_KEY = '@odata.nextLink'
    DATA_VALUES_KEY = 'value'
    APPLICATION_NAME_KEY = 'INSTALLED_SW_NAME'
    BASE_API_URI = '/aternity.odata/v2/INSTALLED_SOFTWARE'
    DISTINCT_VALUES_QUERY = BASE_API_URI + '?$select=' + APPLICATION_NAME_KEY
    PER_APPLICATION_QUERY = BASE_API_URI + '?$filter=(' + APPLICATION_NAME_KEY + " eq '{0}')&$select="
    
    # Parameters
    BASE_URl = ''  # the Aternity rest api base url
    REST_API_USERNAME = ''  # the aternity rest api username
    REST_API_PASSWORD = ''  # the aternity rest api password
    CSV_FILE_PATH = 'install_software.csv'
    COLUMNS_TO_SELECT = ['DEVICE_COUNT',
                         'DEVICE_NAME',
                         'INSTALLED_SW_COUNT',
                         'INSTALLED_SW_INSTALLATION_VOLUME',
                         'INSTALLED_SW_LAST_CHANGE_TIMESTAMP',
                         'INSTALLED_SW_NAME',
                         'INSTALLED_SW_RELATED_TO',
                         'INSTALLED_SW_SCOPE',
                         'INSTALLED_SW_TYPE',
                         'INSTALLED_SW_VENDOR',
                         'INSTALLED_SW_VERSION',
                         'INSTALLED_SW_VERSION_COUNT']
    
    # Globals
    distinct_applications = list()
    csv_file = open(file=CSV_FILE_PATH, mode='w', encoding='utf-8-sig')
    csv_writer = csv.writer(csv_file, lineterminator='\n')
    write_headers = True
    session = requests.session()
    number_of_remaining_apps = 0
    
    
    def installed_software_result_handler(data: List[Dict]):
        global write_headers, csv_writer
        for row in data:
            if write_headers:
                header = row.keys()
                csv_writer.writerow(header)
                write_headers = False
            csv_writer.writerow(row.values())
    
    
    # helper function to safely encode url parameters for the Aternity Rest Api Service
    def encode_url_parameter(parameter: str) -> str:
        parameter = parameter.replace("'", "''")
        return urllib.parse.quote(parameter)
    
    
    # helper function to get the initial query that returns the distinct list of installed applications
    def get_distinct_applications_query() -> str:
        base_url = BASE_URl[:-1] if BASE_URl.endswith('/') else BASE_URl
        if not validators.url(base_url):
            raise ValueError('base url {} is not valid'.format(BASE_URl))
    
        return base_url + DISTINCT_VALUES_QUERY
    
    
    # helper function to get the data per application
    def get_specific_application_query(application: str) -> str:
        base_url = BASE_URl[:-1] if BASE_URl.endswith('/') else BASE_URl
        if not validators.url(base_url):
            raise ValueError('base url {} is not valid'.format(BASE_URl))
    
        return base_url + PER_APPLICATION_QUERY.format(encode_url_parameter(application)) + ",".join(COLUMNS_TO_SELECT)
    
    
    def get_distinct_applications_query_result_handler(data: List[Dict]):
        global distinct_applications, number_of_remaining_apps
        for row in data:
            distinct_applications.append(row[APPLICATION_NAME_KEY])
        number_of_remaining_apps = len(distinct_applications)
    
    
    def query_rest_api(url: str, result_handler: Callable[[List[Dict]], None]):
        global session
        next_link = url
    
        while next_link:
            response = session.get(url=next_link, auth=(REST_API_USERNAME, REST_API_PASSWORD))
            response.encoding = 'utf-8'
            response_json_parsed = response.json()
    
            if DATA_VALUES_KEY not in response_json_parsed:
                raise Exception("Error during aternity rest api response parsing")
    
            data = response_json_parsed[DATA_VALUES_KEY]
            next_link = response_json_parsed[NEXT_PAGE_KEY] if NEXT_PAGE_KEY in response_json_parsed else None
            result_handler(data)
    
    
    def get_installed_software(result_handler: Callable[[List[Dict]], None]):
        global distinct_applications, number_of_remaining_apps
        query_rest_api(get_distinct_applications_query(), get_distinct_applications_query_result_handler)
        for application in distinct_applications:
            print('Number of application left: ' + str(number_of_remaining_apps))
            print('Downloading application ' + application)
            query_rest_api(get_specific_application_query(application), result_handler)
            number_of_remaining_apps = number_of_remaining_apps - 1
    
    
    # Main
    try:
        get_installed_software(installed_software_result_handler)
        print('Finished successfully')
    except Exception as e:
        traceback.print_exc()
        traceback.print_stack()
        print('Stopped due to exception, please contact Aternity support')