It is required to be a Local Administrator of the host to use WAPT’s integrated environment for developing WAPT packages.
We advise you to create/ edit packages in a fully controlled environment that is safe and disposable.
The usage of a disposable virtual host (like Virtualbox) is recommended.
Import the tis-waptdev package in your local repository and install it on your development computer.
1.1.2. Recommendations regarding the test environment¶
The recommended method to correctly test your WAPT packages is to use representative sample of hosts in your inventory.
So the more heterogeneous your installed base of hosts, the larger your sample should be.
This method consists of testing the installation of the package on as many platforms and configurations as possible,
so to improve its reliability, before the WAPT package is transferred to production repositories.
Microsoft Windows host without any Windows update installed: the objective is to detect Windows updates that are required for the software to work properly and to adapt the WAPT package accordingly;
Microsoft Windows host with all the latest Windows updates: the objective is to detect Windows updates that break the package and to adapt the WAPT package accordingly;
Hosts with many installed packages: the objective is to detect a possible dependency with an existing application;
Hosts with many installed packages: the objective is to detect a possible conflict with an existing application;
Install older versions of the software: it is possible that the software installer does not support uninstalling a previous version of the software, in this case, the WAPT package will have to remove older versions of the software before installing the new version;
1.2. Principles of creating package template from the WAPT Console¶
Attention
To create WAPT packages directly from the WAPT Console, it is necessary to have installed the WAPT development environment tis-pyscripter3.
It is NOT recommended to use tis-pyscripter4.
We recommand you to download the waptdev package instead and install it on your computer on which you will create WAPT packages.
1.2.2. Customizing the WAPT package before uploading it to the repository¶
Before uploading a package to your WAPT repository, you may choose to customize its behavior to your Organization’s needs by editing it with PyScripter.
When creating the package template, click on Make and edit ….
Dialog box highlighting the “Make and edit …” button when creating the WAPT package in the WAPT Console¶
Message window showing in the WAPT Console that the WAPT package has been downloaded into the WAPT repository¶
The PyScripter IDE launches automatically to allow you to edit files in the WAPT package.
PyScripter - Customizing a WAPT package within PyScripter¶
The function will test whether a version of the software is already installed on the host using the uninstall key.
If the uninstall key is already present, the new version of the software will be installed only if the installed version is older.
After the installation, the function will finally test that the uninstall key is present and its version, to ascertain that all went well.
List of arguments available with install_msi_if_needed¶
Options (Default Option)
Description
min_version (default None)
Defines the minimal version above which the software will update.
killbefore (default None)
Lists the programs to kill before installing the package.
accept_returncodes (default [0,3010])
Defines the accepted codes other than 0 and 3010 returned by the function.
timeout (default 300)
Defines the maximum installation wait time (in seconds).
properties (default None)
Defines the additional properties to pass as arguments to MSI setup file.
get_version (default None)
Defines the value passed as parameter to control the version number instead of the value returned by the installed_softwares function.
remove_old_version (default False)
Automatically removes an older version of a software whose uninstall key is identical.
force (default False)
Forces the installation of the software even though the same uninstall key has been found.
The wapt-get install_msi_if_needed method searches for an uninstall key in the MSI file properties, it is not necessary to fill it manually in the setup.py file.
You also do not have to fill in killbefore if the value specified in the impacted_process field of the control file is correct.
Note
The setup.py could have looked like this, but the method is less elegant because it does less checking.
To pass additional arguments, store them in a dict.
# -*- coding: utf-8 -*-fromsetuphelpersimport*uninstallkey=[]properties={'SERVER_REGISTER_AS_SERVICE':0,'SERVER_ADD_FIREWALL_EXCEPTION':0,'ADDLOCAL':'Server,Viewer'}definstall():print(u'Installation en cours de TightVNC')install_msi_if_needed('tightvnc-2.8.5-setup-64bit.msi',properties=properties)
Note
The setup.py could have looked like this, but the method is less elegant because it does less checking.
Check the control file content.
Mozilla Firefox-ESR does not comply to industry standards and returns an erroneous version number (it appears to be the installer packaging software version number).
List of arguments available with install_exe_if_needed¶
Options (Default Option)
Description
silentflags (default None)
Defines the silent parameters to pass as arguments to the installer.
key (default None)
Defines the software uninstall key.
min_version (default None)
Defines the minimal version above which the software will update.
killbefore (default None)
Lists the programs to kill before installing the package.
accept_returncodes (default [0,3010])
Defines the accepted codes other than 0 and 3010 returned by the function.
timeout (default 300)
Defines the maximum installation wait time (in seconds).
get_version (default None)
Defines the value passed as parameter to control the version number instead of the value returned by the installed_softwares function. Example https://forum.tranquil.it/viewtopic.php?t=1337.
remove_old_version (default False)
Automatically removes an older version of a software whose uninstall key is identical.
force (default False)
Forces the installation of the software even though the same uninstall key has been found.
The package will then have this behavior:
Firefox will install only if Firefox is not yet installed or if the installed version of Firefox is less than 45.5.0, unless the --force option is passed as argument when installing the package.
On installing, the running firefox.exe processes will be killed (with the value indicated in impacted_process of the control file).
The function will add by itself the uninstall key, so leave the uninstall key argument empty.
When finishing the installation of the package, the function will check that the uninstall key is present on the host and that the version of Firefox is greater than 45.5.0; if this not the case, the package will be flagged as ERROR.
Return codes are used to feed back information on whether a software has installed correctly.
In Windows, the standard successful return code is [0].
If you know that your WAPT packages installs correctly, yet you still get a return code other than [0], then you can explicitly tell WAPT to ignore the error code by using the parameter accept_returncodes.
You can find out how to use the accept_returncodes parameter by exploring this package code.
# -*- coding: utf-8 -*-fromsetuphelpersimport*importreuninstallkey=[]defis_kb_installed(hotfixid):installed_update=installed_windows_updates()if[kbforkbininstalled_updateifkb['HotFixID'].upper()==hotfixid.upper()]:returnTruereturnFalsedefwaiting_for_reboot():# Query WUAU from the registryifreg_key_exists(HKEY_LOCAL_MACHINE,r"SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired")or \
reg_key_exists(HKEY_LOCAL_MACHINE,r"SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending")or \
reg_key_exists(HKEY_LOCAL_MACHINE,r'SOFTWARE\Microsoft\Updates\UpdateExeVolatile'):returnTruereturnFalsedefinstall():kb_files=['windows10.0-kb4522355-x64_af588d16a8fbb572b70c3b3bb34edee42d6a460b.msu',]withEnsureWUAUServRunning():forkb_fileinkb_files:kb_guess=re.findall(r'^.*-(KB.*)-',kb_file)ifnotkb_guessornotis_kb_installed(kb_guess[0]):print('Installing {}'.format(kb_file))run('wusa.exe "{}" /quiet /norestart'.format(kb_file),accept_returncodes=[0,3010,2359302,-2145124329],timeout=3600)else:print('{} already installed'.format(kb_file))ifwaiting_for_reboot():print('A reboot is needed!')
Hint
The full list of Windows Installer Error Messages can be found by visiting this page.
2.1.2.1. Checking the existence of a registry key¶
Command registry_readstring checks if registry key {8A69D345-D564-463c-AFF1-A69D9E530F96} exists in registry path SOFTWARE\Google\Update\Clients of HKEY_LOCAL_MACHINE.
Command registry_readstring reads the value {8A69D345-D564-463c-AFF1-A69D9E530F96} stored in the registry path SOFTWARE\Google\Update\Clients of HKEY_LOCAL_MACHINE.
Command registry_setstring modifies the value of the registry key TOUVersion stored in the registry path SOFTWARE\Microsoft\WindowsLive of HKEY_CURRENT_USER.
With WAPT setuphelper it is possible to create different types of shortcuts.
2.1.3.1. Creating a desktop shortcut for all users¶
Command create_desktop_shortcut creates the shortcut WAPT Console Management into C:\Users\Public directory pointing to C:\ProgramFiles(x86)\wapt\waptconsole.exe; the shortcut is available for all users.
If you restart the installation from PyScripter, you will notice that the “all users” desktop shortcut has disappeared.
2.1.3.3. Creating a menu shortcut for an application¶
Command create_programs_menu_shortcut creates the shortcut WAPT Console Management into start menu pointing to C:\ProgramFiles(x86)\wapt\waptconsole.exe; the shortcut is available for all users.
2.1.3.5. Creating a desktop shortcut for a logged in user¶
Hint
These functions are used in session_setup context.
Command create_user_desktop_shortcut creates the shortcut WAPT Console Management on user desktop pointing to C:\ProgramFiles(x86)\wapt\waptconsole.exe.
2.1.3.7. Creating a menu shortcut to an application for a specific user¶
Hint
These functions are used in session_setup context.
Command create_user_programs_menu_shortcut creates the shortcut WAPT Console Management on user start menu pointing to C:\ProgramFiles(x86)\wapt\waptconsole.exe.
Each command returns a different ProgramFiles location.
For example, command programfiles64 returns native Program Files directory, eg. C:\ProgramFiles(x86) on either win64 or win32 architecture and programfiles() will return the path of the 32bit Program Files directory, eg. ProgramsFiles(x86) on win64 architecture, and ProgramsFiles on win32 architecture.
Command forget_packages informs the WAPT Agent to forget a WAPT package on the selected computer.
Hint
If the desired result is to remove tis-scratch, you should either reinstall the package (wapt-getinstall"tis-scratch") then remove it (wapt-get remove "tis-scratch"), either removing it manually from the Control Panel menu Add/ Remove Programs.
This file MUST be placed in the distribution folder at the root of Firefox.
To help you create this policies.json file you can use the enterprise policy generator generator for Firefox.
When you have generated your policies.json file, place it in c:\waptdev\prefix-firefox-esr-wapt\policies.json.
The distribution folder at the root of Firefox may not exist, so we will test its existence and create it with the mkdirs command if it does not exist:
There is no need to put the full path for the source file since the policies.json file is at the root of the WAPT package, so we use the relative path.
Your package is now ready to apply a configuration.
You can launch an installation with PyScripter and validate that the package works according to your objective.
Finally, launch your Firefox to verify that it will work for your users.
To search unwanted version of Firefox we will use the installed_softwares function.
This function returns a dictionary list containing the software properties:
# -*- coding: utf-8 -*-fromsetuphelpersimport*uninstallkey=[]definstall():#Install firefox if necessaryinstall_exe_if_needed("Firefox Setup 45.5.0esr.exe",silentflags="-ms",key='Mozilla Firefox 45.5.0 ESR (x64 fr)',min_version="45.5.0")#Removal of the firefox shortcut on the all user desktopremove_desktop_shortcut('Firefox')#Creation of the distribution folder if it does not existifnotisdir(r'C:\Program Files\Mozilla Firefox\distribution'):mkdirs(r'C:\Program Files\Mozilla Firefox\distribution')#Copy of the policies.json file found at the root of the package in the destination of the distribution folderfilecopyto('policies.json',r'C:\Program Files\Mozilla Firefox\distribution')#For each Mozilla Firefox installedforuninstallininstalled_softwares('Mozilla Firefox'):#If the software does not have the word ESR in the nameifnot'ESR'inuninstall['name']:print(uninstall['name'])print('Uninstall '+uninstall['key'])#Looking for how we can uninstall it silentlysilent_uninstall=uninstall_cmd(uninstall['key'])print('Run '+silent_uninstall)#We launch the previous command.run(silent_uninstall)#Uninstalling mozilla maintenance serviceforuninstallininstalled_softwares('MozillaMaintenanceService'):run(uninstall_cmd(uninstall['key']))
Your code now handles the uninstallation of unwanted versions of Firefox.
The last example is the best example but this operation only works with Python3.
We can now use variables in our setup.py:
# -*- coding: utf-8 -*-fromsetuphelpersimport*uninstallkey=[]definstall():version_firefox="45.5.0"#Install firefox if necessaryinstall_exe_if_needed("Firefox Setup %sesr.exe"%version_firefox,silentflags="-ms",key='Mozilla Firefox %s ESR (x64 fr)'%version_firefox,min_version=version_firefox)#Removal of the firefox shortcut on the all user desktopremove_desktop_shortcut('Firefox')distribution_folder=r'C:\Program Files\Mozilla Firefox\distribution'#Creation of the distribution folder if it does not existifnotisdir(distribution_folder):mkdirs(distribution_folder)...Therestofthecodedoesnotchange...
Hint
You can retrieve the version number shown in the control file like this:
It is sometimes necessary to customize a software in user context to set specific settings or to comply to the Organization’s rules and preferences:
Creating user desktop shortcut with specific arguments.
Making changes to user Windows registry keys.
Making changes to files, to browser settings of the user.
Configuring shortcuts to the Organization’s set of templates for Documents, Spreadsheets or Presentations in Office Suites to encourage or insure that editorial and graphical guidelines are followed.
Setting up the user’s email or instant messaging from the Organization’s main user data repository (LDAP directory, database, etc).
Customizing an office suite or business software based on the Organization’s main user data repository (LDAP directory, database, etc).
The session_setup function benefits from the power of python to achieve a high level of automation.
Calling that function executes the session_setup script defined within each WAPT package installed on the computer.
The WAPT Agent stores in its local database (C:\ProgramFiles(x86)\wapt\waptdb.sqlite) the instruction sets of all WAPT packages.
Attention
The session_setup script is launched only once per WAPT package version and per user.
The WAPT Agent stores in is local %appdata%\wapt\waptsession.sqlite database the instances of the session_setup scripts that have been already been played.
Output example of wapt-getsession-setupALL:
Note
The logged in user session_setup has already previously been launched.
With session_setup, there is no possibility to call files contained inside the WAPT package.
To call external files when uninstalling, copy and paste the needed files in an external folder during the package installation process (example: c:\cachefile).
2.2.4.3. Example: creating a personalized desktop shortcut¶
One of the possibilities offered by Setuphelpers is adding personalized shortcuts on user desktops, instead of a desktop shortcut common to all users.
For that purpose, we will use the create_user_desktop_shortcut() function to create shortcuts containing the username and passing a website as an argument to Firefox.
This feature is available in the Enterprise version.
The audit function allows to make regular checks to desktop configurations and to centralize the results of these checks in the WAPT Console.
This feature allows you to ascertain that your installed base of hosts matches your set of conformity rules over time.
For example you can:
Regularly check the list of local administrators on the desktops.
Ascertain over time the correct configuration of a critical software.
Regularly check the presence of the correct version of a piece of software.
Ascertain the security settings of a workstation.
The audit function benefits from the depth and the breadth of python libraries for unmatched levels of precision and finesse for your auditing needs.
With the audit function, it is not possible to use files that are contained in the WAPT packages.
To use files embedded in the WAPT package that will be used for an audit, you MUST instruct to copy the file(s) to a temporary folder when the WAPT package installs.
The audit tasks are launched once after every upgrade, then regularly as defined with the audit_schedule value.
The value is contained in the control file of the WAPT package.
By default, if audit_schedule is empty, the audit task can be launched manually from the WAPT Console or be launched automatically if you have defined the option waptaudit_task_period in the wapt-get.ini of the WAPT Agent.
For more information about the last method, please see this documentation.
Otherwise, the periodicity may be indicated in several ways:
An integer (in minutes).
An integer followed by a letter (m = minutes, h = hours , d = days , w = weeks).
By default, the only audit function checks the presence of UninstallKey for its WAPT package.
This way, WAPT ascertains that the software is still present on the host, according to the host configuration.
2.2.6. Auditing configurations to insure compliance¶
Note
This feature is available in the Enterprise version.
The audit_data function allows to make regular checks to desktop configurations and to centralize the results of these checks in the WAPT Console.
There is historization and you can encrypt your data and decrypt it with your WAPT certificate.
For example you can:
Change an administrator password, encrypt information and display it on your WAPT Console.
Regularly check the modification your computer needs like CVE or GLPI inventory.
Ascertain the security settings of a workstation and historize issues.
The audit_data function is usable in the audit function only.
The audit_data functions are launched if they are defined in the defaudit() section of the setup.py file.
On the server side, audit data is stored in the HostAuditData table.
The content of the table can be queried using the Reporting tab in the WAPT Console.
The Data is automatically purged according to expiration date.
When WAPT host update_status() is launched, the newer audit data is sent to the WAPT Server.
On the Client side, the audit data is stored in the host database with an expiration date (date_expiration) and the max count (max_count) of the stored data is defined in the code.
In this example, we are checking public IP on the computer.
Add the audit_data function inside the audit function in the setup.py.
defaudit():ip=wgets('https://api.ipify.org',verify_cert=False)print(f'My public IP address is: {ip}')WAPT.write_audit_data_if_changed('Public IP','log for %s'%get_computername(),ip,max_count=5)return'OK'
Here are the functions related to audit_data:
defwrite_audit_data_if_changed(self,section,key,value,ptype=None,value_date=None,expiration_date=None,max_count=2,keep_days=None):"""Write data only if different from last one"""defwrite_audit_data(self,section,key,value,ptype=None,value_date=None,expiration_date=None,max_count=2,keep_days=None):"""Stores in database a metrics, removes expired ones Args: section (str) key (str) value (any) value_date expiration_date (str) : expiration date of the new value max_count (int) : keep at most max_count value. remove oldest one. keep_days (int) : set the expiration date to now + keep_days days. override expiration_date arg if not None Returns: None"""defread_audit_data(self,section,key,default=None,ptype=None):"""Retrieve the latest value associated with section/key from database"""defread_audit_data_set(self,section,key):"""Retrieve all the values associated with section/key from database"""defdelete_audit_data(self,section,key):defread_audit_data_since(self,last_query_date=None):"""Retrieve all the values since a date from database"""
This part of the documentation is for advanced users of WAPT.
The update_package functions are very practical, they allow to gain a lot of time when needing to update a WAPT package with the most recent version of a piece of software.
Here is the update_package script for firefox-esr as an example:
defupdate_package():importre,requests,glob#Retrieving the last file nameurl=requests.head('https://download.mozilla.org/?product=firefox-esr-latest&os=win64',proxies={}).headers['Location']filename=url.rsplit('/',1)[1].replace('%20',' ')#download of it if is not in the packageifnotisfile(filename):print('Downloading %s from %s'%(filename,url))wget(url,filename)#removing old exe with wrong nameforfninglob.glob('*.exe'):iffn!=filename:remove_file(fn)# updates control version from filename, increment package version.control.version='%s-0'%(re.findall('Firefox Setup (.*)esr\.exe',filename)[0])control.save_control_to_wapt()
The file C:\waptdev\tis-adwcleaner-wapt is created.
Here you will find an example of a portable package that takes almost all the WAPT functions of a setup.py:
fromsetuphelpersimport*uninstallkey=[]exe_name='AdwCleaner.exe'path_adw=makepath(programfiles,'AdwCleaner')path_exe=makepath(path_adw,exe_name)nameshortcut='AdwCleaner'definstall():mkdirs(path_adw)filecopyto(exe_name,path_exe)create_desktop_shortcut(nameshortcut,path_exe)defuninstall():remove_tree(path_adw)remove_desktop_shortcut(nameshortcut,path_exe)defaudit():ifnotisfile(path_exe):print('File not found')return"OK"else:print('File Found')return"ERROR"defupdate_package():wget('https://downloads.malwarebytes.com/file/AdwCleaner',exe_name)control.version=get_file_properties(exe_name)['FileVersion']+'-0'control.save_control_to_wapt()
Create a WAPT package template from the downloaded .msu file.
In the WAPT Console, click on Tools ‣ Package Wizard.
PyScripter - WAPT Console window for creating a package template¶
Select the downloaded .msu package and fill in the required fields.
Informations required for creating the MSU package¶
Click on Make and edit (recommended) to launch package customization.
WAPT package IDE is launched using the source code from the pre-defined .msu template.
As usual with WAPT packages, test, then build, then sign, then upload and finally affect the desired WAPT packages to your selected hosts and it is done!!
If the KB becomes bundled with the following Patch Tuesday, you can select the hosts onto which the package has been applied and forget the KB package on the hosts.
You have to give a description to the WAPT package, define the os_target and the version of the WAPT package.
Hint
os_target for unix is linux.
Warning
The software version number in your control file MUST start at 0, and not the version number of the software title, as the version number may not be the same as displayed in the DEB / YUM repository.
It is to be noted that a sub-version -1 has been added.
It is the packaging version of the WAPT package.
It allows the WAPT package Developer to release several WAPT package versions of the same software, very useful for very rapid and iterative development.
Make changes to the code in the setup.py file accordingly.
Make changes to the setup.py file with an uninstall.
defuninstall():apt_remove('vlc')
Launch a remove from VSCode Run Configurations.
After uninstallation, the software is correctly removed¶
Check that the software has been correctly removed.
dpkg-l|grepvlc
Hint
In the uninstall() function, it is not possible to call for files included inside the WAPT package.
To call files from the package, it is necessary to copy/ paste the files in a temporary directory during package installation.
With WAPT, the integrity of the package is ensured. A package whose content has been modified without being re-signed will systematically be refused by the WAPT client.
On the other hand, the content of a WAPT package is not encrypted and will be readable by everyone.
This technical model of transparency brings nevertheless many benefits.
This can be annoying in the case of a package that contains a password, a license key, or any sensitive or confidential data.
You will find here an example of a WAPT package where we encrypt a string of text in an update_package function and then decrypt this text in the install function.
In this example, the update_package function allows us to browse the WAPT Server database to retrieve the certificate from each host and then encrypt the sensitive text with it.
The encrypted text for each host is then stored in a encrypt-txt.json file at the root of the WAPT package.
When the WAPT package installs, the WAPT Agent will take the encrypted text and decipher it with his own private key.
You can test it by yourself by downloading the example package tis-encrypt-sample.
Attention
The python output (log install of the WAPT package) is readable by the users on the host, so you should not display the deciphered text with a print during installation.
3. Using different IDEs for developing WAPT packages¶
Natively supported text editors in WAPT on Windows¶
Text editor name
Text editor logo
PyScripter
Visual Studio Code
Visual Studio Codium
To configure another editor for WAPT, you MUST modify the editor_for_packages attribute in the [global] section of your WAPT Console’s %LOCALAPPDATA%\waptconsole\waptconsole.ini configuration file.
Natively supported text editors in WAPT on Windows¶
Text editor name
Text editor logo
Visual Studio Code
Visual Studio Codium
Nano
Vim
To configure another editor for WAPT, you MUST modify the editor_for_packages attribute in the [global] section of your WAPT Agent configuration file: /opt/wapt/wapt-get.ini.
By default, if the editor_for_packages attribute is empty, WAPT will try to launch (in that order):