To create and customize WAPT packages, follow that documentation and you will quickly become a master of WAPT.

Package structure detailed

A WAPT package is a .zip file containing several things:

WAPT package structure

WAPT package structure

  • a file setup.py;

  • one or several binary files;

  • some additional optional files;

  • a control file in the WAPT folder;

  • a icon.png file in the WAPT folder;

  • a certificate.crt file in the folder WAPT;

  • a manifest.sha256 file in the folder WAPT;

  • a signature.sha256 file in the folder WAPT;

  • 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

The control file is the identity card of a package.

package           : tis-firefox-esr
version           : 62.0-0
architecture      : all
section           : base
priority          : optional
maintainer        : Administrateur
description       : Firefox Web Browser French
description_fr    : Navigateur Web Firefox Français
description_es    : Firefox Web Browser
depends           :
conflicts         :
maturity          : PROD
locale            : fr
target_os         : windows
min_os_version    :
max_os_version    :
min_wapt_version  : 1.6.2
sources           :
installed_size    :
impacted_process  : firefox.exe
audit_schedule    :
editor            : Mozilla
keywords          : Navigateur
licence           : MPL
homepage          : https://www.mozilla.org/en-US/firefox/organizations/
signer            : Tranquil IT
signer_fingerprint: 459934db53fd804bbb1dee79412a46b7d94b638737b03a0d73fc4907b994da5d
signature         : MLOzLiz0qCHN5fChdylnvXUZ8xNJj4rEu5FAAsDTdEtQ(...)hsduxGRJpN1wLEjGRaMLBlod/p8w==
signature_date    : 20170704-164552
signed_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
Description of options of the control file

Settings

Description

Example value

package

Package name

tis-geogebra

version

Package version, can not contain more than 5 delimiters, the last number being the version number of the packaging

5.0.309.0-1

architecture

Processor architecture

x64

section

Package type (host, group, base)

base

priority

Package install priority (optional)

Not mandatory for the moment

maintainer

Author of the package

Arnold Schwarzenegger <terminator@mydomain.lan>

description

Package description that will appear in the console and on the web interface

The Graphing Calculator for Functions, Geometry, Algebra, Calculus, Statistics and 3D

description_fr

Localized description of the package

Calculatrice graphique

depends

Packages that must be installed before installing the package

tis-java

conflicts

Packages that must be uninstalled before installing the package

tis-graph

maturity

Maturity level (BETA, DEV, PROD)

PROD

locale

Language environment for the package

fr,en,es

target_os

Accepted Operating System for the package

windows,mac,linux

min_os_version

Minimum version of Windows for the package to be seen by the WAPT agent

6.0

max_os_version

Maximum version of Windows for the package to be seen by the WAPT agent

10.0

min_wapt_version

WAPT’s minimal version for the package to work properly

1.3.8

sources

Path to the SVN location of the package (source command)

https://srv-svn.mydomain.lan/sources/tis-geogebra-wapt/trunk/

installed_size

Minimum required free disk space to install the package

254251008

impacted_process

Indicates a list of impacted processes when installing a package

firefox.exe

audit_schedule

Periodicity of execution of the audit function in the WAPT package

60

editor

Editor of the software package

Mozilla

license

Reference of the software license

GPLV3

keywords

Set of keywords describing the WAPT package

Productivity,Text Processor

homepage

Official homepage of the software embedded in the WAPT package

https://www.tranquil.it/

signer

CommonName (CN) of the package’s signer

Tranquil IT

signer_fingerprint

Fingerprint of the certificate holder’s signature

2BAFAF007C174A3B00F12E9CA1E74956

signature

SHA256 hash of the package

MLOzLiz0qCHN5fChdylnvXUZ8xNJj4rEu5FAAsDTdEtQ(…)hsduxGRJpN1wLEjGRaMLBlod/p8w==

signature_date

Date when the package was signed

20180307-230413

signed_attributes

List of package’s attributes that are signed

package, version, architecture, section, priority, maintainer, description, depends, conflicts, maturity, locale, min_wapt_version, sources, installed_size, signer, signer_fingerprint, signature_date, signed_attributes

Attention

If the control file contains special characters, the control file must be saved in UTF-8 (No BOM) format.

PyScripter - UTF-8 (No BOM)

PyScripter - UTF-8 (No BOM)

Fields details

package

WAPT package name, without any accent, nor space, nor any special or uppercase character.

version

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.

architecture

New in version 1.5.

Defines whether the package may be installed on x64 or x32 processor equipped computers.

Note

A x64 package will be invisible for a WAPT agent installed on a x86 machine.

Allowed values:

  • x86: the package is designed for 32bit computers;

  • x64: the package is designed for 64bit computers;

  • all: the package is designed for 32bit or 64bit computers;

section

  • host: host package;

  • group: group package;

  • base: software package;

  • unit: OU package;

priority

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.

maintainer

Defines the WAPT package creator.

Note

To define the WAPT package creator’s email address may be useful.

Use Firstname LASTNAME <email@example.com> format.

description

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.

depends

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 (,).

depends: tis-java,tis-firefox-esr,tis-thunderbird

conflicts

Works as the opposite of depends.

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 (,).

conflicts: tis-firefox

maturity

Defines the maturity of a package.

By default, WAPT agents will see packages flagged as PROD and packages with an empty maturity.

For a computer to see packages with different maturity levels, you will have to configure the maturities attribute in wapt-get-ini of the WAPT agent.

locale

Defines the language of the WAPT package.

A WAPT agent will see by default packages that are configured for its language environment(s) and packages with no language specified.

For a computer to see a package in another language, you will have to configure the locales in wapt-get.ini of the WAPT agent.

locales = fr,en,es

The language filled in the field must be in ISO 639-1 format.

target_os

Defines the Operating System for the package.

A WAPT agent will see by default packages that are configured for its operating system and packages with no operating system specified.

Since version 1.8 the field target_os can either be windows, macos, linux or left empty.

min_os_version

For a windows target_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.

max_os_version

For a windows target_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.

min_wapt_version

WAPT minimum version to install a package

Note

With functionalities in WAPT evolving, some functions that you may have used in old packages may become obsolete with newer versions of WAPT agents.

sources

Defines a SVN repository, for example:

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.

installed_size

Defines the required minimum free disk space to install the package.

installed_size: 254251008

The testing of available free disk space is done on the C:\Program Files folder.

The value set in installed_size must be in bytes.

Hint

To convert storage values to bytes, visit https://bit-calculator.com/.

impacted_process

Indicates processes that are impacted when installing a package.

Example:

impacted_process : firefox.exe,chrome.exe,iexplorer.exe

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.

audit_schedule

Periodicity of execution of audit checks.

audit_schedule : 60

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);

editor

New in version 1.6.

Software editor of the binaries embedded in the WAPT base package.

editor: Mozilla

The values may be used as filters in the WAPT console and with the self-service.

keywords

New in version 1.6.

Keyword list to categorize the WAPT package.

keywords: office

The values may be used as filters in the WAPT console and with the self-service.

license

New in version 1.6.

Reference of the software license for the embedded software binaries.

license : GPLV3

The values may be used as filters in the WAPT console and with the self-service.

homepage

New in version 1.6.

Official homepage of the software binaries embedded in the WAPT package.

homepage : https://wapt.fr

The values may be used as filters in the WAPT console and with the self-service.

signer

Automatically filled during package signature.

CN of the certificate. It is typically the signer’s full name.

signer_fingerprint

Automatically filled during package signature.

Private key fingerprint of the package signer.

signature

Automatically filled during package signature.

Signature of the attributes of the package.

signature_date

Automatically filled during package signature.

Date when the attributes of the package have been signed.

signed_attributes

Automatically filled during package signature.

List of the package’s attributes that are signed.

The setup.py file

import setuphelpers

That line is found at the beginning of every WAPT package that embeds a setup.py:

from setuphelpers import *

The package imports all setuphelpers functions.

Setuphelpers is a WAPT library that offers many methods to more easily develop highly functional packages.

uninstallkey list

We then find:

uninstallkey = ['tisnaps2','Mozilla Firefox 45.6.0 ESR (x86 fr)']

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:

uninstallkey = []

Function install()

Then comes the setup.py function declaration.

It is the recipe of the WAPT package, the set of instructions that will be executed.

def install():
    run('"install.exe" /S')

The wapt.psproj file

Package project file wapt.psproj is found in the WAPT folder.

It’s the PyScripter project file for the WAPT package.

To edit a package with PyScripter, just open the file.

The icon.png file

The icon.png icon file is located in the WAPT folder.

It associates an icon to the package.

That icon will appear in the local web interface of WAPT self-service (http://127.0.0.1:8088).

Hint

The icon must be a 48px per 48px PNG file.

The manifest.sha256 file

The manifest.sha256 manifest file is located in the WAPT folder.

It contains the sha256 fingerprint of every file in the WAPT package.

The signature file

The signature file is located in the WAPT folder.

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:\Program Files (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;

Other files

Other files may be embedded in the WAPT package. For example:

  • an installer beside your setup.py to be called in your setup.py;

  • an answer file to pass on to the software installer;

  • a license file;

Python 2 to Python 3

Attention

With WAPT 2.0, the WAPT internals have switched to python3. WAPT packages must also follow the new python3 syntax.

The principale syntax differences

Syntax

Python 2

Python 3

print

print'Hello'

print('Hello')

unicode string

ur

r

operators

<> <=> !=

!=

Windows registry access

_winreg

winreg

Setting up your WAPT development and test environment

Prerequisites

Attention

  • it is required to be a Local Administrator of the machine 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 machine (like Virtualbox) is recommended;

  • import the tis-pyscripter package in your local repository and install it on your development computer;

Recommendations regarding the test environment

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.

Testing method

Operating systems and architectures

  • Windows XP;

  • Windows 7;

  • Windows 10;

  • Windows Server 2008 R2;

  • Windows Server 2012 and R2;

  • x86;

  • x64;

  • Physical and virtual machines;

  • laptops;

Hint

When possible, RC and Beta version of Operating Systems should be tested (ex: Windows 10 Creators Update).

State of Windows Updates

  • Microsoft Windows machine 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 machine with all the latest Windows updates: the objective is to detect Windows updates that break the package and to adapt the WAPT package accordingly;

State of software installations

  • Machines with many installed packages: the objective is to detect a possible dependency with an existing application;

  • Machines 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;

Principes 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.

  • download the 7-zip MSI installer:

  • create a WAPT package Template from the installer;

    In the WAPT console, click on Tools ‣ Make package template from setup file:

    Pyscripter - WAPT console window for creating a package template

    Pyscripter - WAPT console window for creating a package template

    Select the downloaded MSI setup file and fill in the required fields. Verify that the package name does not contains any version number.

    Informations required for creating the package

    Informations required for creating the package

  • two solutions are available:

    • click on Make and edit …. (recommended) to launch package customization;

    • click on Build and upload to directly build and upload the package;

    Attention

    The button Build and upload directly uploads the package into the private repository without testing.

    This method works relatively well with MSI installers because their installation is more standardized.

    However, the second method that consists of first testing locally the package before uploading is the recommended method.

Customize the package before make and edit

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

PyScripter - Informations required for creating the package

PyScripter - The package has been created

PyScripter - The package has been created

The PyScripter IDE allows to edit files in the WAPT package.

PyScripter - Customizing a package with Pyscripter

PyScripter - Customizing a package with Pyscripter

Presentation of Pyscripter

PyScripter project explorer

PyScripter - project explorer

PyScripter - project explorer

The PyScripter project explorer lists the different files that you might need, notably the control file and the setup.py file.

Run Configurations

PyScripter - Run commands in the PyScripter project explorer

PyScripter - Run commands in the PyScripter project explorer

The Run option in the project explorer of:program:PyScripter will allow you to launch actions on the packages that you are editing.

Editor panel

PyScripter - Editor panel

PyScripter - Editor panel

The edition panel in PyScripter allows to edit the setup.py file and the control file.

Python Console

PyScripter - Python console in PyScripter

PyScripter - Python console in PyScripter

This is the python console visible in PyScripter, it will allow you to display the python output when you execute Run commands.

You can also use it to test/ debug portions of your script setup.py.

To learn more about the composition of a wapt package, visit the documentation on the structure of a WAPT package detailed.

Testing locally the installation of the WAPT package

You can then test the launch of an installation on your development station.

Running an install command from the Pyscripter console

Running an install command from the Pyscripter console

The PyScripter Console allows you to check whether the installation went well.

Testing locally the uninstallation of the WAPT package

You can then test the launch of an uninstallation on your development station.

Running a remove command from the Pyscripter console

Running a remove command from the Pyscripter console

The PyScripter Console allows you to check whether the uninstallation went well.

Packaging .msi packages (example)

For this example we will take tightvnc.

You can download it here: https://www.tightvnc.com/download.php

Now, you can then generate your package template, please refer to the documentation for creating packages from the WAPT console.

Edit your control file (architecture, impacted_process, target_os, description, maintainer …), please refer to the documentation on the control file structure.

Your pyscripter opens, go to your setup.py:

# -*- coding: utf-8 -*-
from setuphelpers import *

uninstallkey = []

def install():
    print('installing tis-tightvnc')
    install_msi_if_needed('tightvnc-2.8.5-setup-64bit.msi')
  • the function will test whether a version of the software is already installed on the machine 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:

# -*- coding: utf-8 -*-
from setuphelpers import *

uninstallkey = ["{8B9896FC-B4F2-44CD-8B6E-78A0B1851B59}"]

def install():
    print('installing tis-tightvnc')
    run('msiexec /norestart /q /i "tightvnc-2.8.5-setup-64bit.msi"')

Run the installation and see what happens when the software is already installed.

wapt-get -ldebug install C:\waptdev\tis-tightvnc-wapt
Installing WAPT file C:\waptdev\tis-tightvnc-wapt
MSI tightvnc-2.8.5-gpl-setup-64bit.msi already installed. Skipping msiexec

Results:

=== install packages ===
C:\waptdev\tis-tightvnc-wapt   | tis-tightvnc (2.8.5.0-1)

Passing additional arguments

To pass additional arguments, store them in a dict.

# -*- coding: utf-8 -*-
from setuphelpers import *

uninstallkey = []

properties = {
    'SERVER_REGISTER_AS_SERVICE':0,
    'SERVER_ADD_FIREWALL_EXCEPTION':0,
    }

def install():
    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:

# -*- coding: utf-8 -*-
from setuphelpers import *

uninstallkey = ["{8B9896FC-B4F2-44CD-8B6E-78A0B1851B59}"]

def install():
    print('installing tis-tightvnc')
    run('msiexec /norestart /q /i "tightvnc-2.8.5-setup-64bit.msi" SERVER_REGISTER_AS_SERVICE=0 SERVER_ADD_FIREWALL_EXCEPTION=0')

Video demonstration

Packaging .exe packages (example)

  • download the .exe installer from a reliable source;

    Download the installer in exe format Firefox ESR x64 on https://download.mozilla.org/?product=firefox-esr-latest-ssl&os=win64

  • look up documentation relating to silent flags;

  • then generate your package template, please refer to the documentation for creating packages from the WAPT console. PyScripter loads up and opens the .exe package project.

    PyScripter opening with focus on the *control* file

    PyScripter opening with focus on the control file

  • edit your control file (architecture, impacted_process, target_os, description, maintainer …), please refer to the documentation on the control file structure;

  • 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).

    • original control file:

      package           : tis-firefox-esr
      version           : 4.42.0.0-0
      architecture      : all
      section           : base
      priority          : optional
      maintainer        : user
      description       : automatic package for firefox setup 52.6.0esr
      impacted_process  : 
      
    • modified control file:

      package           : tis-firefox-esr
      version           : 52.6.0-1
      architecture      : all
      section           : base
      priority          : optional
      maintainer        : Tranquil-IT Systems
      description       : Mozilla Firefox 52.6.0 ESR
      impacted_process  : firefox.exe
      

    Note

    It is to be noted that a sub-version -1 has been appended to the software version number; it is the packaging version of the WAPT package.

    It allows the Package Developer to release several WAPT package versions of the same software, very useful for very rapid and iterative development.

Using install_exe_if_needed

The function is slightly the same as that used with .msi installers, with some differences:

  • the function requires to pass the silent flag as an argument;

  • the function requires to pass the uninstall key as an argument;

# -*- coding: utf-8 -*-
from setuphelpers import *

uninstallkey = []

def install():
    print('installing tis-firefox-esr')
    install_exe_if_needed("Firefox Setup 45.5.0esr.exe",silentflags="-ms",key='',min_version="4.42.0.0")
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;

Finding the uninstallation key

Unlike .msi files, the key to uninstall an .exe is not in the file properties.

So you need to install the software first to know the uninstall key.

Therefore you must start once the installation from pyscripter with the run configuration and then install.

_images/run_install.png

Once the software is installed, go to the WAPT console, then find your development machine.

In the software inventory tab find your software and copy the value indicated in the uninstallkey column.

Retrieving an uninstallkey from the console

Retrieving an uninstallkey from the console

You must also check the value of the version with the value indicated in min_version in your setup.py.

Modify your setup.py with the new parameters:

# -*- coding: utf-8 -*-
from setuphelpers import *

uninstallkey = []

def install():
    print('installing tis-firefox-esr')
    install_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")

To test that your key works correctly, you must run an installation again in pyscripter.

_images/run_install.png

WAPT should not attempt to install the software because it is already present, the following message should display:

>>>
*** Remote Interpreter Reinitialized ***
Command Line : install "c:\waptdev\tis-firefox-esr_x64_PROD_fr-wapt\WAPT\.."
Using config file: C:\Program Files (x86)\wapt\wapt-get.ini
Installing WAPT files c:\waptdev\tis-firefox-esr_x64_PROD_fr-wapt
Exe setup Firefox_Setup_78.7.1esr.exe already installed. Skipping

Results:

=== install packages ===
c:\waptdev\tis-firefox-esr_x64_PROD_fr-wapt | tis-firefox-esr (78.7.1-102)

Now you can now test the uninstallation:

Test the uninstallation

You can now build and upload your package, please refer to the documentation for build and upload packages from the WAPT console.

Note

If you leave the uninstallkey blank, uninstalling your package will not work.

Special case of a non-silent uninstaller

In some particular cases, a package using install_exe_if_needed fills in the uninstall key, but the uninstall key points to a non silent uninstaller.

We have to circumvent that problem by using a function that will remove the uninstall key at the end of the installation.

:emphasize-lines: 13

# -*- coding: utf-8 -*-
from setuphelpers import *

uninstallkey = []

def install():
    install_exe_if_needed("setup.exe",
                          silentflags="/s",
                          key='{D9E87643-0005-447E-9111-78697A9C1595}',
                          min_version="14.0")
    uninstallkey.remove('{D9E87643-0005-447E-9111-78697A9C1595')

def uninstall():
    run(r'"C:\Program Files\Kutl\uninstall.exe" /supersilent')

Hint

The uninstall feature can also be used to run code in addition to uninstalling software, ex: delete folder, delete shortcut …

Video demonstration

Building the package and sending it to the WAPT server

  • once the package is ready, build it and send it to the WAPT server, In waptconsole:

Option "build-upload in waptconsole"

Option “Build and upload package” in waptconsole

  • select the package in the c:\waptdev folder;

_images/build-upload-console-select-folder.PNG
  • confirm the selected package;

_images/build-upload-console-confirm.PNG

You have just uploaded your first wapt package.

Warning

Once your package has uploaded, refresh the package list using the Refresh packages list button or by pressing F5 on your keyboard.

Working with non standard return codes

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 -*-
from setuphelpers import *
import re

uninstallkey = []

def is_kb_installed(hotfixid):
    installed_update = installed_windows_updates()
    if [kb for kb in installed_update if kb['HotFixID' ].upper() == hotfixid.upper()]:
        return True
    return False

def waiting_for_reboot():
    # Query WUAU from the registry
    if reg_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'):
        return True
    return False

def install():
    kb_files = [
        'windows10.0-kb4522355-x64_af588d16a8fbb572b70c3b3bb34edee42d6a460b.msu',
        ]
    with EnsureWUAUServRunning():
      for kb_file in kb_files:
          kb_guess = re.findall(r'^.*-(KB.*)-',kb_file)
          if not kb_guess or not is_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))

      if waiting_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.

Testing and manipulating folders and files

Creating a path recursively

Command makepath

makepath(programfiles,'Mozilla','Firefox')

… makes the path variable for C:\Program Files (x86)\Mozilla\Firefox.

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=makepath#setuphelpers.makepath

Creating and destroying directories

Command mkdirs

mkdirs('C:\\test')

… creates the directory C:\test.

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=mkdirs#setuphelpers.mkdirs

Command remove_tree

remove_tree(r'C:\tmp\target')

… destroys the directory C:\tmp\target.

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=remove_tree#setuphelpers.remove_tree

Checking if a path is a file or a folder

Command isdir

isdir(makepath(programfiles32,'software')):
    print('The directory exists')

… checks if C:\Program Files (x86)\software is a directory.

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=isdir#setuphelpers.isdir

Command isfile

isfile(makepath(programfiles32,'software','file')):
    print('file exist')

… checks if C:\Program Files (x86)\software\file is a file.

Hint

For more informations or to learn more on arguments on that function,* please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=isfile#setuphelpers.isfile

Check if a directory is empty

Command dir_is_empty

dir_is_empty(makepath(programfiles32,'software')):
    print('dir is empty')

… checks that directory C:\Program Files (x86)\software is empty.

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=dir_is_empty#setuphelpers.dir_is_empty

Copying a file

Command filecopyto

filecopyto('file.txt',makepath(programfiles32,'software'))

… copies file.txt into the C:\Program Files (x86)\software directory.

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=filecopyto#setuphelpers.filecopyto

Copying a directory

Command copytree2

copytree2('sources','C:\\projet')

… copies the sources folder into the C:\projet directory.

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=copytree2#setuphelpers.copytree2

Retrieving the version of a file

Command get_file_properties

get_file_properties(makepath(programfiles32,'InfraRecorder','infrarecorder.exe'))['ProductVersion']

… shows package properties.

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=get_file_properties#setuphelpers.get_file_properties

Manipulating registry keys

Checking the existence of a registry key

Command registry_readstring

if registry_readstring(HKEY_LOCAL_MACHINE, "SOFTWARE\\Google\\Update\\Clients\\{8A69D345-D564-463c-AFF1-A69D9E530F96}", 'pv'):
    print('key exist')

… checks if registry key {8A69D345-D564-463c-AFF1-A69D9E530F96} exists in registry path SOFTWARE\Google\Update\Clients of HKEY_LOCAL_MACHINE.

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=registry_readstring#setuphelpers.registry_readstring

Showing the value of a registry key

Command registry_readstring

print(registry_readstring(HKEY_LOCAL_MACHINE, r'SOFTWARE\Google\Update\Clients\{8A69D345-D564-463c-AFF1-A69D9E530F96}', 'pv'))

… reads the value {8A69D345-D564-463c-AFF1-A69D9E530F96} stored in the registry path SOFTWARE\Google\Update\Clients of HKEY_LOCAL_MACHINE.

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=registry_readstring#setuphelpers.registry_readstring

Modifying the value of a registry key

Command registry_setstring

registry_setstring(HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\Windows Live\\Common",'TOUVersion','16.0.0.0', type=REG_SZ)

… modifies the value of the registry key TOUVersion stored in the registry path SOFTWARE\Microsoft\Windows Live of HKEY_CURRENT_USER.

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=registry_setstring#setuphelpers.registry_setstring

Creating and destroying shortcuts

A simple shortchut

A menu folder shortcut

A desktop shortcut

create_desktop_shortcut

Command create_desktop_shortcut

create_desktop_shortcut(r'WAPT Console Management',target=r'C:\Program Files (x86)\wapt\waptconsole.exe')

… creates the shortcut WAPT Console Management into C:\Users\Public directory pointing to C:\Program Files (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:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=create_desktop_shortcut#setuphelpers.create_desktop_shortcut

remove_desktop_shortcut

Command remove_desktop_shortcut

remove_desktop_shortcut('WAPT Console Management')

… deletes the WAPT Console Management shortcut from the folder C:\Users\Public; the shortcut is deleted for all users.

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=remove_desktop_shortcut#setuphelpers.remove_desktop_shortcut

Firefox places a shortcut on the all users desktop, we are going to delete it.

We will use the remove_desktop_shortcut function:

  • modify your setup.py and use the function like this:

    # -*- coding: utf-8 -*-
    from setuphelpers import *
    
    uninstallkey = []
    
    def install():
        install_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")
        remove_desktop_shortcut('Firefox')
    
  • if you restart the installation from pyscripter, you will notice that the “all users” desktop shortcut has disappeared;

A current user desktop shortcut

create_user_desktop_shortcut

Hint

These functions are used in session_setup context.

Command create_user_desktop_shortcut

create_user_desktop_shortcut(r'WAPT Console Management',target=r'C:\Program Files (x86)\wapt\waptconsole.exe')

… creates the shortcut WAPT Console Management on user desktop pointing to C:\Program Files (x86)\wapt\waptconsole.exe.

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=create_user_desktop_shortcut#setuphelpers.create_user_desktop_shortcut

remove_user_desktop_shortcut

Command remove_user_desktop_shortcut

remove_user_desktop_shortcut('WAPT Console Management')

… deletes the WAPT Console Management shortcut from the logged in user’s desktop.

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=remove_user_desktop_shortcut#setuphelpers.remove_user_desktop_shortcut

Windows environment/ Software/ Services

Windows version check

Command windows_version

windows_version()<Version('6.2.0'):

… checks that the Windows version is strictly inferior to 6.2.0.

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=windows_version#setuphelpers.windows_version

Visit also Microsoft Windows version number.

Check if 64bits architecture

Command iswin64

if iswin64():
    print('Pc x64')
else:
    print('Pc not x64')

… checks that the system architecture is 64bits.

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=iswin64#setuphelpers.iswin64

Program Files variable

programfiles/ programfiles32/ programfiles64

Return different ProgramFiles locations

Command programfiles64

print(programfiles64())

… returns native Program Files directory, eg. C:\Program Files (x86) on either win64 or win32 architecture.

print(programfiles())

… returns path of the 32bit Program Files directory, eg. Programs Files (x86) on win64 architecture, and Programs Files on win32 architecture.

print(programfiles32())

AppData variable

user_appdata/ user_local_appdata

Hint

These functions are used with session_setup

Command user_appdata

print(user_appdata())

… returns roaming AppData profile path of logged on user (C:\Users\%username%\AppData\Roaming).

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=user_appdata#setuphelpers.user_appdata

Command user_local_appdata

print(user_local_appdata())

… returns the local AppData profile path of the logged on user (C:\Users\%username%\AppData\Local).

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=user_local_appdata#setuphelpers.user_local_appdata

Disable temporarily the wow3264 file redirector

Command disable_file_system_redirection

with disable_file_system_redirection():
    filecopyto('file.txt',system32())

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=disable_file_system_redirection#setuphelpers.disable_file_system_redirection

Disable wow3264 redirection in the current context

Get current user

Command get_current_user

print(get_current_user())

… shows the currently logged on username

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=get_current_user#setuphelpers.get_current_user

Get computer name

Command get_computername

print(get_computername())

… shows the name of the computer

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=get_computername#setuphelpers.get_computername

Get computer domain

Command get_domain_fromregistry

get_domain_fromregistry()

… returns the FQDN of the computer.

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=get_domain_fromregistry#setuphelpers.get_domain_fromregistry

Action on installed software

Check installed software

Command installed_softwares

installed_softwares('winscp')

… returns the list of installed software on the computer from registry in an array.

[{'install_location': u'C:\\Program Files\\WinSCP\\', 'version': u'5.9.2', 'name': u'WinSCP 5.9.2', 'key': u'winscp3_is1', 'uninstall_string': u'"C:\\Program Files\\WinSCP\\unins000.exe"', 'publisher': u'Martin Prikryl', 'install_date': u'20161102', 'system_component': 0}]

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=installed_softwares#setuphelpers.installed_softwares

Get uninstall command with registry

Command uninstall_cmd

uninstall_cmd('winscp3_is1')

… returns the silent uninstall command.

"C:\Program Files\WinSCP\unins000.exe" /SILENT

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=uninstall_cmd#setuphelpers.uninstall_cmd

Uninstalling software

for soft in installed_softwares('winscp3'):
    if Version(soft['version']) < Version('5.0.2'):
        run(WAPT.uninstall_cmd(soft['key']))
  • for each item of the list return by installed_softwares containing keyword winscp;

  • if the version is lower than 5.0.2;

  • then uninstall using the uninstall_cmd and specifying the corresponding uninstallkey;

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://dev.tranquil.it/sphinxdocs/source/setuphelpers.html?highlight=uninstall_cmd#setuphelpers.uninstall_cmd

Kill task

Command killalltasks

killalltasks('firefox')

… kills the process named Firefox.

Hint

For more informations or to learn more on arguments on that function, please visit official Setuphelpers reference documentation:

https://www.wapt.fr/en/api-doc-1.5/source/setuphelpers.html?highlight=killalltasks#setuphelpers.killalltasks

Using control file fields

It’s possible to use control file informations on setup.py

Get packages version

def setup():
    print(control['version'])

… shows the version value from the control file.

def setup():
    print(control['version'].split('-',1)[0])

… shows the software version number without the WAPT version number from the control file.

Get software name

Manage WAPT package by another WAPT package

Installing a package

Command install

WAPT.install('tis-scratch')

… installs tis-scratch on the computer.

Removing a package

Command remove

WAPT.remove('tis-scratch')

… uninstalls tis-scratch from the computer.

Forgetting a package

Command forget_packages

WAPT.forget_packages('tis-scratch')

… 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-get install "tis-scratch") then remove it (wapt-get remove "tis-scratch"), either removing it manually from the Control Panel menu Add/ Remove Programs.

Improving my package

Copying a file

It is possible to configure Firefox with a policies.json file. See https://github.com/mozilla/policy-templates/blob/master/README.md.

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 this extension: https://addons.mozilla.org/fr/firefox/addon/enterprise-policy-generator/.

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:

if not isdir(r'C:\Program Files\Mozilla Firefox\distribution'):
    mkdirs(r'C:\Program Files\Mozilla Firefox\distribution')

Important

If you have backslashes in your path, you should always put an r in front of the string, like in the previous example.

You will also need to use the filecopyto function to copy the policies.json file:

filecopyto('policies.json',r'C:\Program Files\Mozilla Firefox\distribution')

Hint

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.

Modify your setup.py:

# -*- coding: utf-8 -*-
from setuphelpers import *

uninstallkey = []

def install():
    install_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")
    remove_desktop_shortcut('Firefox')

    if not isdir(r'C:\Program Files\Mozilla Firefox\distribution'):
        mkdirs(r'C:\Program Files\Mozilla Firefox\distribution')

    filecopyto('policies.json',r'C:\Program Files\Mozilla Firefox\distribution')

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.

Uninstalling unwanted versions

Hint

At each step of these examples you can run an installation to test the result.

In our case we want to uninstall the non ESR version of Firefox.

We will look for the other software installed on the machine to check if a non-esr version of Firefox is installed.

To reproduce our example, download and install the consumer version here: https://download.mozilla.org/?product=firefox-latest-ssl&os=win:

  • to search unwanted version of Firefox we will use the installed_softwares function. This function returns a dictionary list containing the software properties:

    print(installed_softwares('Firefox'))
    
    [
       {
         'install_date': '',
       'install_location': 'C:\\Program Files\\Mozilla Firefox',
       'key': 'Mozilla Firefox 78.7.1 ESR (x64 fr)',
       'name': 'Mozilla Firefox 78.7.1 ESR (x64 fr)',
       'publisher': 'Mozilla',
       'system_component': 0,
       'uninstall_string': '"C:\\Program Files\\Mozilla Firefox\\uninstall\\helper.exe"',
       'version': '78.7.1',
       'win64': True
         },
    
       {
          'install_date': '',
        'install_location': 'C:\Program Files (x86)\\Mozilla Firefox',
        'key': 'Mozilla Firefox 79.0 (x86 fr)',
        'name': 'Mozilla Firefox 79.0 (x86 fr)',
        'publisher': 'Mozilla',
        'system_component': 0,
        'uninstall_string': '"C:\Program Files (x86)\\Mozilla Firefox\\uninstall\\helper.exe"',
        'version': '79.0',
        'win64': False
          }
    ]
    
  • check the name of each software:

    for uninstall in installed_softwares('Mozilla Firefox'):
        print(uninstall['name'])
    
  • show the name of each software found:

    for uninstall in installed_softwares('Mozilla Firefox'):
        if not 'ESR' in uninstall['name']
            print(uninstall['name'])
    
  • show the name of each software found which does not include the string ESR in its name and its uninstallkey:

    for uninstall in installed_softwares('Mozilla Firefox'):
        if not 'ESR' in uninstall['name']
            print(uninstall['name'])
            print('Uninstall ' + uninstall['key'])
    

We will now use a WAPT trick using the uninstall_cmd function:

  • install cmd accepts an uninstall key as an argument and will send the command to run to start the silent uninstall:

    for uninstall in installed_softwares('Mozilla Firefox'):
        if not 'ESR' in uninstall['name']:
            print(uninstall['name'])
            print('Uninstall ' + uninstall['key'])
            silent_uninstall = uninstall_cmd(uninstall['key'])
            print('Run ' + silent_uninstall)
    
  • start the uninstallation:

    for uninstall in installed_softwares('Mozilla Firefox'):
        if not 'ESR' in uninstall['name']:
            print(uninstall['name'])
            print('Uninstall ' + uninstall['key'])
            silent_uninstall = uninstall_cmd(uninstall['key'])
            print('Run ' + silent_uninstall)
            run(silent_uninstall)
    

We can also uninstall the mozilla maintenance service:

for uninstall in installed_softwares('MozillaMaintenanceService'):
    run(uninstall_cmd(uninstall['key']))
  • finally, modify your setup.py:

    # -*- coding: utf-8 -*-
    from setuphelpers import *
    
    uninstallkey = []
    
    def install():
        #Install firefox if necessary
        install_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 desktop
        remove_desktop_shortcut('Firefox')
    
        #Creation of the distribution folder if it does not exist
        if not isdir(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 folder
        filecopyto('policies.json',r'C:\Program Files\Mozilla Firefox\distribution')
    
        #For each Mozilla Firefox installed
        for uninstall in installed_softwares('Mozilla Firefox'):
            #If the software does not have the word ESR in the name
            if not 'ESR' in uninstall['name']:
                print(uninstall['name'])
                print('Uninstall ' + uninstall['key'])
    
                #Looking for how we can uninstall it silently
                silent_uninstall = uninstall_cmd(uninstall['key'])
                print('Run ' + silent_uninstall)
    
                #We launch the previous command.
                run(silent_uninstall)
    
        #Uninstalling mozilla maintenance service
        for uninstall in installed_softwares('MozillaMaintenanceService'):
            run(uninstall_cmd(uninstall['key']))
    

Your code now handles the uninstallation of unwanted versions of Firefox.

Improving setup.py to use variables

Examples of variable usage:

version_firefox = "45.0"

uninstallkey = "Mozilla Firefox " + version_firefox + " ESR (x64 fr)"
print(uninstallkey)

uninstallkey = "Mozilla Firefox %s ESR (x64 fr)" % (version_firefox)
print(uninstallkey)

uninstallkey = "Mozilla Firefox {} ESR (x64 fr)".format(version_firefox)
print(uninstallkey)

uninstallkey = f"Mozilla Firefox {version_firefox} ESR (x64 fr)"
print(uninstallkey)

Important

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 -*-
from setuphelpers import *

uninstallkey = []

def install():

    version_firefox = "45.5.0"

    #Install firefox if necessary
    install_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 desktop
    remove_desktop_shortcut('Firefox')

    distribution_folder=r'C:\Program Files\Mozilla Firefox\distribution'

    #Creation of the distribution folder if it does not exist
    if not isdir(distribution_folder):
        mkdirs(distribution_folder)

    ... The rest of the code does not change ...

Hint

You can retrieve the version number shown in the control file like this:

version_firefox = control.get_software_version()

Customizing the user environment

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.

Principles of session_setup

The WAPT: command:session_setup function is executed for each user using this command:

C:\Program Files (x86)\wapt\wapt-get.exe session-setup ALL

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:\Program Files (x86)\wapt\waptdb.sqlite) the instruction sets of all WAPT packages.

Attention

session_setup 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 session_setup that have been already been played.

Output example of wapt-get session-setup ALL:

Note

the connected user’s session_setup had already been launched.

wapt-get session-setup ALL

Configuring tis-7zip ... No session-setup. Done
Configuring tis-ccleaner ... Already installed. Done
Configuring tis-vlc ... No session-setup. Done
Configuring tis-tightvnc ... No session-setup. Done
Configuring tis-paint.net ... No session-setup. Done
Configuring wsuser01.mydomain.lan ... No session-setup. Done

Using session_setup

The session_setup scripts are located in the section def session_setup() of the setup.py file:

Example:

def session_setup():
   registry_setstring(HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\Windows Live\\Common",'TOUVersion','16.0.0.0', type=REG_SZ)

Attention

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.

# -*- coding: utf-8 -*-
from setuphelpers import *

uninstallkey = []

def install():
    install_exe_if_needed("Firefox Setup 45.5.0esr.exe",silentflags="-ms",key='Mozilla Firefox 45.4.0 ESR (x64 fr)',min_version="45.5.0")

def session_setup():
  create_user_desktop_shortcut("Mozilla Firefox de %s" % get_current_user(),r'C:\Program Files\Mozilla Firefox\firefox.exe',arguments="-url https://tranquil.it")
  • now start the session-setup directly from pyscripter:

    Pyscripter - running session-setup

    Pyscripter - running session-setup

  • finally, check that the icon is present on the desktop;

Using the audit functions for compliance

Note

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.

Working principle

The audit tasks are launched once after every upgrade, then regularly as defined with the audit_schedule value.

To manually launch an audit check, you may also use the following command:

wapt-get audit

Note

By default, the audit function will not launch if the audit is not necessary.

To force the execution, you may launch the following command:

wapt-get audit -f

How to write

The audit script is defined in the package’s setup.py with a function def audit():

In this example, we are improving the firefox package previously studied in this documentation.

  • add the audit function in the setup.py:

    def audit():
        if isfile(r'C:\Program Files\Mozilla Firefox\distribution\policies.json'):
            print('File policies.json found')
            return "OK"
        else:
            print('File policies.json not found')
            return "ERROR"
    
  • start the audit from pyscripter:

    Pyscripter - Running an audit

    Pyscripter - Running an audit

  • test with the file then delete the C:\Program Files\Mozilla Firefox\distribution\policies.json file and test again with pyscripter;

You can directly see the status of the audit in the console (Click on the package then on the audit column):

Checking an audit status in the WAPT console

Checking an audit status in the WAPT console

The audit function returns one of these 3 values:

  • OK;

  • WARNING;

  • ERROR;

Attention

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.

Planning an audit

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 your package.

By default, if audit_schedule is empty, the audit task will need to be launched manually or from the WAPT console.

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);

Default behavior of the audit function

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.

Updating automatically a software package

Note

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.

Working principle

The update_package function will:

  • fetch online the latest version of the software;

  • download the latest version of the software binaries;

  • remove old versions of the software binaries;

  • update the version number of the software in the control file;

If you base your install function on the version number inside the control file, then you do not even need to modify your setup.py.

You just have to do your usual Quality Assurance tests before you build-upload your new package.

Example

Here is the update_package script for firefox-esr as an example:

def update_package():
      import re,requests,glob

      #Retrieving the last file name
      url = 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 package
      if not isfile(filename):
          print('Downloading %s from %s'%(filename,url))
          wget(url,filename)

      #removing old exe with wrong name
      for fn in glob.glob('*.exe'):
          if fn != 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()

You may launch the update_package in PyScripter:

Pyscripter - Running an update-package-source

Pyscripter - Running an update-package-source

You will find many inspiring examples of update_package scripts in packages hosted in the Tranquil IT store.

Deploying a portable software with WAPT

A good example of a WAPT package is a self-contained/ portable software package:

  • create the folder for the software in C:\Program Files (x86);

  • copy the software in that folder;

  • create the shortcut to the application;

  • manage the uninstallation process for the application;

  • close the application if it’s running;

Example with ADWCleaner

First, download Adwcleaner: https://downloads.malwarebytes.com/file/adwcleaner.

You can then generate your package template, please refer to the documentation for creating packages from the WAPT console.

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:

from setuphelpers import *

uninstallkey = []

exe_name = 'AdwCleaner.exe'
path_adw = makepath(programfiles,'AdwCleaner')
path_exe = makepath(path_adw,exe_name)
nameshortcut = 'AdwCleaner'

def install():
    mkdirs(path_adw)
    filecopyto(exe_name,path_exe)
    create_desktop_shortcut(nameshortcut,path_exe)

def uninstall():
    remove_tree(path_adw)
    remove_desktop_shortcut(nameshortcut,path_exe)

def audit():
    if not isfile(path_exe):
        print('File not found')
        return "OK"
    else:
        print('File Found')
        return "ERROR"

def update_package():
    wget('https://downloads.malwarebytes.com/file/AdwCleaner',exe_name)
    control.version = get_file_properties(exe_name)['FileVersion'] + '-0'
    control.save_control_to_wapt()

Packaging Windows Update .msu packages

Hint

Pre-requisites: to build WAPT packages, the WAPT development environment must be installed;

Between Patch Tuesday releases, Microsoft may release additional KBs or critical updates that will need to be pushed to hosts quickly.

For that purpose, WAPT provides a package template for .msu files.

In that example, we use the KB4522355 downloaded from Microsoft Catalog website.

Creating a MSU package template from the WAPT console

  • 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

    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

    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;

Packaging simple Linux packages

Before starting, we assume several conditions:

  • you have a graphical interface on your Linux system that you use for developing and testing packages;

  • you have installed the vscode package from the Tranquil IT repository;

  • your user is named linuxuser and is a member of the sudoers group;

Creating a base template from you linux computer

  • start up a Command Line utility;

  • as linuxuser, create a WAPT package template;

    wapt-get make-template <template_name>
    

    Warning

    Do not launch this command as root or with sudo.

    When you create a template, there will be several files in the folder .vscode inside your package folder:

    • settings.json;

    • launch.json;

    Example with VLC:

    wapt-get make-template "tis-vlc"
    
    Using config file: /opt/wapt/wapt-get.ini
    Template created. You can build the WAPT package by launching
    /opt/wapt//wapt-get.py build-package /home/linuxuser/waptdev/tis-vlc-wapt
    You can build and upload the WAPT package by launching
    /opt/wapt//wapt-get.py build-upload /home/linuxuser/waptdev/tis-vlc-wapt
    

    Hint

    All packages are stored in linuxuser’s home.

VSCode loads up and opens package project.

VSCode opening with focus on the *setup* file

VSCode opening with focus on the setup file

  • check the control file content;

    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.

    • original control file:

      package           : tis-vlc
      version           : 0-0
      architecture      : all
      section           : base
      priority          : optional
      maintainer        : user
      description       : automatic package for vlc
      
    • modified control file:

      package           : tis-vlc
      version           : 0
      architecture      : all
      section           : base
      priority          : optional
      maintainer        : Tranquil-IT Systems
      description       : VLC for linux
      target_os         : linux
      min_wapt_version  : 1.8
      

    Note

    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 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;

    :emphasize-lines: 8
    # -*- coding: utf-8 -*-
    from setuphelpers import *
    
    uninstallkey = []
    
    def install():
        apt_install('vlc')
    
  • save the package;

Managing the uninstallation

  • make changes to the setup.py file with an uninstall;

def uninstall():
apt_remove('vlc')
  • launch a remove from VSCode Run Configurations;

    After uninstallation, the software is correctly removed

    After uninstallation, the software is correctly removed

  • check that the software has been correctly removed;

    dpkg -l | grep vlc
    

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.

Managing the session-setup

  • make changes to the setup.py file with a session-setup;

    In this example, we will create a file: file: vlcrc by default in the user profile.

    def session_setup():
      vlcrc_content="""[qt] # Qt interface
    qt-notification=0
    qt-privacy-ask=0
    metadata-network-access=0
    """
    
      vlcdir = os.path.join(os.environ['HOME'], '.config', 'vlc')
      path_vlrc = makepath(vlcdir,'vlcrc')
      ensure_dir(vlcdir)
      if not isfile(path_vlrc):
          with open(makepath(vlcdir,'vlcrc')) as f:
              f.write(vlcrc_content)
    
  • launch a session-setup from VSCode Run Configurations;

    After uninstallation, the software is correctly removed

    After uninstallation, the software is correctly removed

Building and uploading the package

You will find your package here: ~/waptdev.

You need to transfer the package folder to the Windows machine that has the private key.

Then, please refer to the documentation for building and uploading packages from the WAPT console.

Encrypting sensitive data contained in a WAPT package

Note

This part of the documentation is for advanced users of WAPT.

This feature is available only in the Enterprise version.

What is the purpose for doing that?

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.

Fortunately, we have a solution!

Working principle

When a WAPT agent registers with the WAPT server, it generates a private key/ public certificate pair in C:\Program Files (x86)\wapt\private.

  • The certificate is sent to the server with the inventory when the WAPT client is first registered;

  • The private key is kept by the agent and is only readable locally by Local Administrators;

We will therefore encrypt the sensitive data contained inside the package with the certificate belonging to the machine.

During installation, the WAPT agent will be able to decrypt the sensitive data using its private key.

With this mode of operation, the WAPT server and secondary repositories have no knowledge of the sensitive data.

Practical case

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 -*-
from setuphelpers import *
import json
from waptcrypto import SSLCertificate
import waptguihelper
import base64


def install():
    encryptlist = json.loads(open('encrypt-txt.json','r').read())
    if WAPT.host_uuid in encryptlist:
        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)


def update_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)))
    for value in data['result']:
        if value['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.

On Windows

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

Editor name

editor_for_packages value

PyScripter

None

Microsoft Visual Studio Code

vscode or code

Microsoft Visual Studio Codium

vscodium or codium

Example config in waptconsole.ini:

[global]
...
editor_for_packages=vscode

On Linux / macOS

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

Editor name

editor_for_packages value

Microsoft Visual Studio Code

vscode or code

Microsoft Visual Studio Codium

vscodium or codium

Nano

nano

Vim

vim

Vi

vi

[global]
...
editor_for_packages=vim

Configuring WAPT to use a custom editor

On Windows

Custom editors can be used, for example Notepad++ or PyCharm on Windows.

Using a custom text editor on Windows

Editor name

editor_for_packages value

Notepad++

C:\Program Files\Notepad++\notepad++.exe setup_filename

PyCharm

C:\Program Files\JetBrains\PyCharm Community Edition 2019.3.2\bin\pycharm64.exe wapt_sources_dir

[global]
...
editor_for_packages=C:\Program Files\Notepad++\notepad++.exe {setup_filename}

On Linux/ macOS

Custom editors can be used, for example PyCharm.

Using a custom text editor on linux / macos

Editor name

editor_for_packages value

PyCharm

/opt/pycharm/bin/pycharm_x64 wapt_sources_dir

[global]
...
editor_for_packages=/opt/pycharm/bin/pycharm_x64 {wapt_sources_dir}

Custom arguments

Arguments for the editor_for_packages option

Argument

Description

{setup_filename}

Launches custom editor and edit WAPT package setup.py file

{control_filename}

Launches custom editor and edit WAPT package control file

{wapt_sources_dir}

Launches custom editor and opens WAPT package folder

{wapt_base_dir}

Launches custom editor and opens WAPT install folder