a wapt.psproj file in the folder WAPT,
this file is used to store the PyScripter configuration data
for the WAPT package;
since WAPT 1.8, a hidden .vscode folder that contains
a launch.json and a settings.json file
used to store the VScode configuration data for the WAPT package;
The control file is the identity card of a package.
package:tis-firefox-esrversion:62.0-0architecture:allsection:basepriority:optionalmaintainer:Administrateurdescription:Firefox Web Browser Frenchdescription_fr:Navigateur Web Firefox Françaisdescription_es:Firefox Web Browserdepends:conflicts:maturity:PRODlocale:frtarget_os:windowsmin_os_version:max_os_version:min_wapt_version:1.6.2sources:installed_size:impacted_process:firefox.exeaudit_schedule:editor:Mozillakeywords:Navigateurlicence:MPLhomepage:https://www.mozilla.org/en-US/firefox/organizations/signer:Tranquil ITsigner_fingerprint:459934db53fd804bbb1dee79412a46b7d94b638737b03a0d73fc4907b994da5dsignature:MLOzLiz0qCHN5fChdylnvXUZ8xNJj4rEu5FAAsDTdEtQ(...)hsduxGRJpN1wLEjGRaMLBlod/p8w==signature_date:20170704-164552signed_attributes:package,version,architecture,section,priority,maintainer,description,depends,conflicts,maturity,locale,min_os_version,max_os_version,min_wapt_version,sources,installed_size,signer,signer_fingerprint,signature_date,signed_attributes
Preferably, always start with the packaged software version (digits only)
split by points (.) and finish with the WAPT packaging version separated
by a dash (-) character.
This option is not supported at this time. That field will be used
to define package installation priority. This feature will become useful
to define mandatory security updates.
Describes the functionality of the package that will appear in the console
and on the local web interface http://127.0.0.1:8088.
Hint
Adding a field description_fr or description_es allows you
to internationalize the description of your package.
If the language does not exist, the WAPT agent will use
the default language description.
Defines the packages that must be installed before, for example tis-java
is a dependency for the LibreOffice package and tis-java must be installed
before LibreOffice.
Several dependencies may be defined by splitting them with commas (,).
conflicts defines package(s) that must be removed before installing a package,
for example tis-firefox must be removed before the package tis-firefox-esr
is installed, or OpenOffice must be removed before LibreOffice is installed.
Several conflicts may be defined by splitting them with commas (,).
For a windowstarget_os, this field defines the minimal
Windows Operating System Version.
For example, this attribute may be used to avoid installing
on WindowsXP packages that only work on Windows7 and above.
Since version 1.8, it can also define the minimal Mac OS version.
We advise not to use it with Linux since there are several
different distributions.
For a windowstarget_os, it defines the maximal
Windows Operating System Version.
For example, this attribute may be used to install on Windows7
more recent versions of a software that are no more supported on Windows XP.
Since version 1.8, it can also define the minimal Mac OS version.
We advise not to use it with Linux since there are several
different distributions.
This method allows to version a package and collaboratively work on it.
Hint
Package versionning is particularly useful when several people create
packages in a collaborative way. This function is also useful to trace
the history of a package if you are subject to Regulations in your industry.
This field is used by the functions install_msi_if_needed
and install_exe_if_needed if killbefore has not been filled.
impacted_process is also used when uninstalling a package.
This allows to close the application if the application is running
before being uninstalled.
We associate here a list of uninstall keys to the package.
When a package is removed, the WAPT agent looks up the uninstallkey
in the registry associated to the package. This uninstallkey will indicate
to WAPT the actions to trigger to remove the software.
Even if there is no uninstallkey for a software,
it is mandatory to declare an empty uninstallkey array:
It contains the signature of the manifest.sha256 file.
On installing a package, wapt-get checks:
that the signature of manifest.sha256 matches the actual
manifest.sha256 file (the agent will verify the public certificates
in C:\ProgramFiles(x86)\wapt\ssl on Windows
and /opt/wapt/ssl on Linux and MacOS);
that the sha256 fingerprint of each file is identical to the fingerprint
in the manifest.sha256 file;
The recommended method to correctly test your WAPT packages is to use
a representative sample of machines in your inventory. So the more heterogeneous
your installed base of machines, the larger your sample must 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;
Principles of creating package template from the WAPT console¶
Hint
To create WAPT packages directly from the WAPT console, it is necessary
to have installed the WAPT development environment tis-pyscripter.
Creating a package template from the WAPT console¶
In that example, we use the 7-zip MSI setup downloaded
from the 7-zip official website.
Customizing the package before uploading it to your 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 …..
PyScripter - Informations required for creating the package¶
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_exe_if_needed¶
Settings
Default value
Description
msi
name of the .msi file to execute.
min_version
None
minimal version above which the software will update.
killbefore
[]
list of programs to kill before installing the package.
accept_returncodes
[0,3010]
accepted codes other than 0 and 3010 returned by the function.
timeout
300
maximum installation wait time (in seconds).
properties
{}
additional properties to pass as arguments to MSI setup file.
get_version
None
value passed as parameter to control the version number instead of
the value returned by the installed_softwares function
remove_old_version
False
automatically removes an older version of a software
whose uninstall key is identical
force
False
forces the installation of the software even though
the same uninstall key has been found
Note
The 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.
Hint
You also don’t 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,}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¶
Settings
Default value
Description
exe
Name of the .exe file to execute.
silentflags
Silent parameters to pass as arguments to the installer.
key
None
Software uninstall key.
min_version
None
Minimal version above which the software will update.
killbefore
[ ]
List of programs to kill before installing the package.
accept_returncodes
[0,3010]
Accepted codes other than 0 and 3010 returned by the function.
timeout
300
Maximum installation wait time (in seconds).
get_version
None
Value passed as parameter to control the version number
instead of the value returned by the installed_softwares
function.
remove_old_version
False
Automatically removes an older version of a software whose uninstallkey
is identical
force
False
Forces the installation of the software even though the same uninstallkey
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 machine 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.
Simple examples of commonly used setuphelper functions¶
Presentation of several functions implemented in Setuphelpers
and frequently used to develop WAPT packages.
… 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.
Hint
For more informations or to learn more on arguments on that function,
please visit official Setuphelpers reference documentation:
… informs WAPT to forget tis-scratch 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-getremove"tis-scratch"), either removing it manually from
the Control Panel menu Add/ Remove Programs.
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.
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).
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 machines
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 first copy the file(s) in a temporary folder
during package installation.
This part of the documentation is for advanced users of WAPT.
update_package functions are very practical, they allow to gain
a lot of time when it is time 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 you package, give the os_target
and the version of your 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, as we don’t know precisely
from apt/yum repo which version number it will be.
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 machine
and then encrypt the sensitive text with it.
The encrypted text for each machine is then stored in a encrypt-txt.json
file at the root of the package.
During the installation of the package, the WAPT agent will take the encrypted
text and decrypt it with its private key.
# -*- coding: utf-8 -*-fromsetuphelpersimport*importjsonfromwaptcryptoimportSSLCertificateimportwaptguihelperimportbase64definstall():encryptlist=json.loads(open('encrypt-txt.json','r').read())ifWAPT.host_uuidinencryptlist:host_key=WAPT.get_host_key()v=base64.b64decode(encryptlist[WAPT.host_uuid])encrypttxt=host_key.decrypt(v).decode('utf-8')print(r"Here is the deciphered text: %s"%encrypttxt)else:error("%s not found in encrypt-txt.json"%WAPT.host_uuid)defupdate_package():urlserver=inifile_readstring(makepath(install_location('WAPT_is1'),'wapt-get.ini'),'global','wapt_server').replace('https://','')encrypttxt=input("Enter the text to be encrypted: ")encryptlist={}credentials_url=waptguihelper.login_password_dialog('Credentials for wapt server',urlserver,'admin','')data=json.loads(wgets('https://%s:%s@%s/api/v1/hosts?columns=host_certificate&limit=10000'%(credentials_url['user'],credentials_url['password'],urlserver)))forvalueindata['result']:ifvalue['host_certificate']:host_cert=SSLCertificate(crt_string=value['host_certificate'])encryptlist[value['uuid']]=base64.b64encode(host_cert.encrypt(encrypttxt.encode('utf-8'))).decode('utf-8')print(value['computer_fqdn']+':'+value['uuid']+':'+encryptlist[value['uuid']])open('encrypt-txt.json','w').write(json.dumps(encryptlist))
Attention
The python output (log install of the package) is readable by the users
on the machine, so you should not display the decrypted text
with a print during installation.
Using different IDEs for developing WAPT packages¶
If you are used to work with another IDE,
you can be relieved now as WAPT supports other editors.
Some code editors are natively supported:
PyScripter;
VSCode;
VSCodium;
Other editors can be selected and will be launched when you create
a new template for a WAPT package from WAPT Console.
Note
Using a supported IDE will launch the WAPT package project
with a valid debug configuration.
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.
Alternative methods for editing WAPT packages on Windows¶
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):
vscodium;
vscode;
nano;
vim;
vi.
Alternative methods for editing WAPT packages on Linux¶