Windows Package Manager

Introduction

Salt provides a Windows package management tool for installing, updating, removing, and managing software packages on remote Windows systems. This tool provides a software repository and a package manager similar to what is provided by yum and apt on Linux. The repository contains a collection of package definition files.

What are package definition files?

A package definition file is a YAML/JINJA2 file with a .sls file extension that contains all the information needed to install software using Salt. It defines:

  • Full name of the software package

  • The version of the software package

  • Download location of the software package

  • Command-line switches for silent install and uninstall

  • Whether or not to use the Windows task scheduler to install the package

Package definition files can be hosted in one or more Git repositories. The .sls files used to install Windows packages are not distributed by default with Salt. You have to initialize and clone the default repository salt-winrepo-ng which is hosted on GitHub by SaltStack. The repository contains package definition files for many common Windows packages and is maintained by SaltStack and the Salt community. Anyone can submit a pull request to this repo to add new package definitions.

You can manage the package definition file through either Salt or Git. You can download software packages from either a git repository or from HTTP(S) or FTP URLs. You can store the installer defined in the package definition file anywhere as long as it is accessible from the host running Salt.

You can use the Salt Windows package manager like yum on Linux. You do not have to know the underlying command to install the software.

  • Use pkg.install to install a package using a package manager based on the OS the system runs on.

  • Use pkg.installed to check if a particular package is installed in the minion.

Note

The Salt Windows package manager does not automatically resolve dependencies while installing, updating, or removing packages. You have to manage the dependencies between packages manually.

Quickstart

This quickstart guides you through using the Windows Salt package manager (winrepo) to install software packages in four steps:

  1. (Optional) Install libraries

  2. Populate the local Git repository

  3. Update minion database

  4. Install software packages

Install libraries

(Optional) If you are using the Salt Windows package manager with package definition files hosted on a Salt Git repo, install the libraries GitPython or pygit2.

Populate the local Git repository

The SLS files used to install Windows packages are not distributed by default with Salt. Assuming no changes to the default configuration (file_roots), initialize and clone salt-winrepo-ng repository.

salt-run winrepo.update_git_repos

On successful execution of winrepo.update_git_repos, the winrepo repository is cloned on the master in the location specified in winrepo_dir_ng and all package definition files are pulled down from the Git repository.

On a masterless minion, use salt-call to initialize and clone the salt-winrepo-ng

salt-call --local winrepo.update_git_repos

On successful execution of the runner, the winrepo repository is cloned on the minion in the location specified in winrepo_dir_ng and all package definition files are pulled down from the Git repository.

Update minion database

Run pkg.refresh_db on all Windows minions to create a database entry for every package definition file and build the package database.

# From the master
salt -G 'os:windows' pkg.refresh_db

# From the minion in masterless mode
salt-call --local pkg.refresh_db

The pkg.refresh_db command parses the YAML/JINJA package definition files and generates the database. The above command returns the following summary denoting the number of packages that succeeded or failed to compile:

local:
    ----------
    failed:
        0
    success:
        301
    total:
        301

Note

This command can take a few minutes to complete as all the package definition files are copied to the minion and the database is generated.

Note

You can use pkg.refresh_db when writing new Windows package definitions to check for errors in the definitions against one or more Windows minions.

Install software package

You can now install a software package using pkg.install:

# From the master
salt * pkg.install 'firefox_x64'

# From the minion in masterless mode
salt-call --local pkg.install "firefox_x64"

The above command installs the latest version of Firefox on the minions.

Configuration

The Github repository (winrepo) is synced to the file_roots in a location specified by the winrepo_dir_ng setting in the config. The default value of winrepo_dir_ng is as follows:

  • Linux master: /srv/salt/win/repo-ng (salt://win/repo-ng)

  • Masterless minion: C:\salt\srv\salt\win\repo-ng (salt://win/repo-ng)

Master Configuration

The following settings are available for configuring the winrepo on the master:

See here for detailed information on all master config options for winrepo.

winrepo_dir

winrepo_dir (str)

This setting is maintained for backwards compatibility with legacy minions. It points to the location in the file_roots where the winrepo files are kept. The default is: /srv/salt/win/repo

winrepo_dir_ng

winrepo_dir_ng (str)

The location in the file_roots where the winrepo files are kept. The default is /srv/salt/win/repo-ng.

Warning

You can change the location of the winrepo directory. However, it must always be set to a path that is inside the file_roots. Otherwise, the software definition files will be unreachable by the minion.

Important

A common mistake is to change the file_roots setting and fail to update the winrepo_dir_ng and winrepo_dir settings so that they are inside the file_roots

winrepo_remotes

winrepo_remotes (list)

This setting is maintained for backwards compatibility with legacy minions. It points to the legacy git repo. The default is a list containing a single URL:

https://github.com/saltstack/salt-winrepo

The legacy repo can be disabled by setting it to an empty list in the master config.

winrepo_remotes: []

winrepo_remotes_ng

winrepo_remotes_ng (list)

This setting tells the winrepo.update_git_repos command where the next generation winrepo is hosted. This a list of URLs to multiple git repos. The default is a list containing a single URL:

https://github.com/saltstack/salt-winrepo-ng

winrepo_refspecs

winrepo_refspecs (list)

Specify what references to fetch from remote repositories. The default is ['+refs/heads/*:refs/remotes/origin/*', '+refs/tags/*:refs/tags/*']

winrepo_branch

winrepo_branch (str)

The branch of the git repo to checkout. The default is master

winrepo_provider

winrepo_provider (str)

The provider to be used for winrepo. Default is pygit2. Falls back to gitpython when pygit2 is not available

winrepo_ssl_verify

winrepo_ssl_verify (bool)

Ignore SSL certificate errors when contacting remote repository. Default is False

Master Configuration (pygit2)

The following configuration options only apply when the winrepo_provider option is set to pygit2.

winrepo_insecure_auth

winrepo_insecure_auth (bool)

Used only with pygit2 provider. Whether or not to allow insecure auth. Default is False

winrepo_passphrase

winrepo_passphrase (str)

Used only with pygit2 provider. Used when the SSH key being used to authenticate is protected by a passphrase. Default is ''

winrepo_privkey

winrepo_privkey (str)

Used only with pygit2 provider. Used with winrepo_pubkey to authenticate to SSH remotes. Default is ''

winrepo_pubkey

winrepo_pubkey (str)

Used only with pygit2 provider. Used with winrepo_privkey to authenticate to SSH remotes. Default is ''

winrepo_user

winrepo_user (str)

Used only with pygit2 provider. Used with winrepo_password to authenticate to HTTPS remotes. Default is ''

winrepo_password

winrepo_password (str)

Used only with pygit2 provider. Used with winrepo_user to authenticate to HTTPS remotes. Default is ''

Minion Configuration

Refreshing the package definitions can take some time, these options were introduced to allow more control of when it occurs. These settings apply to all minions whether in masterless mode or not.

winrepo_cache_expire_max

winrepo_cache_expire_max (int)

Sets the maximum age in seconds of the winrepo metadata file to avoid it becoming stale. If the metadata file is older than this setting, it will trigger a pkg.refresh_db on the next run of any pkg module function that requires the metadata file. Default is 604800 (1 week).

Software package definitions are automatically refreshed if stale after winrepo_cache_expire_max. Running a highstate forces the refresh of the package definitions and regenerates the metadata, unless the metadata is younger than winrepo_cache_expire_max.

winrepo_cache_expire_min

winrepo_cache_expire_min (int)

Sets the minimum age in seconds of the winrepo metadata file to avoid refreshing too often. If the metadata file is older than this setting, the metadata will be refreshed unless you pass refresh: False in the state. Default is 1800 (30 min).

winrepo_cachefile

winrepo_cachefile (str)

The file name of the winrepo cache file. The file is placed at the root of winrepo_dir_ng. Default is winrepo.p.

winrepo_source_dir

winrepo_source_dir (str)

The location of the .sls files on the Salt file server. Default is salt://win/repo-ng/.

Warning

If the default for winrepo_dir_ng is changed, then this setting will also need to be changed on each minion. The default setting for winrepo_dir_ng is /srv/salt/win/repo-ng. If that were changed to /srv/salt/new/repo-ng then the winrepo_source_dir would need to be changed to salt://new/repo-ng

Masterless Minion Configuration

The following settings are available for configuring the winrepo on a masterless minion:

See here for detailed information on all minion config options for winrepo.

winrepo_dir

winrepo_dir (str)

This setting is maintained for backwards compatibility with legacy minions. It points to the location in the file_roots where the winrepo files are kept. The default is: C:\ProgramData\Salt Project\Salt\srv\salt\win\repo

winrepo_dir_ng

winrepo_dir_ng (str)

The location in the file_roots where the winrepo files are kept. The default is C:\ProgramData\Salt Project\Salt\srv\salt\win\repo-ng.

Warning

You can change the location of the winrepo directory. However, it must always be set to a path that is inside the file_roots. Otherwise, the software definition files will be unreachable by the minion.

Important

A common mistake is to change the file_roots setting and fail to update the winrepo_dir_ng and winrepo_dir settings so that they are inside the file_roots. You might also want to verify winrepo_source_dir on the minion as well.

winrepo_remotes

winrepo_remotes (list)

This setting is maintained for backwards compatibility with legacy minions. It points to the legacy git repo. The default is a list containing a single URL:

https://github.com/saltstack/salt-winrepo

The legacy repo can be disabled by setting it to an empty list in the minion config.

winrepo_remotes: []

winrepo_remotes_ng

winrepo_remotes_ng (list)

This setting tells the winrepo.update_git_repos command where the next generation winrepo is hosted. This a list of URLs to multiple git repos. The default is a list containing a single URL:

https://github.com/saltstack/salt-winrepo-ng

Sample Configurations

Masterless

The configs in this section are for working with winrepo on a Windows minion using salt-call --local.

Default Configuration

This is the default configuration if nothing is configured in the minion config. The config is shown here for clarity. These are the defaults:

file_roots:
  base:
    - C:\ProgramData\Salt Project\Salt\srv\salt
winrepo_source_dir: 'salt://win/repo-ng'
winrepo_dir_ng: C:\ProgramData\Salt Project\Salt\srv\salt\win\repo-ng

The winrepo.update_git_repos command will clone the repository to win\repo-ng on the file_roots.

Multiple Salt Environments

This starts to get a little tricky. The winrepo repository doesn't get cloned to each environment when you run winrepo.update_git_repos, so to make this work, all environments share the same winrepo. Applying states using the saltenv option will find the state files in the appropriate environment, but the package definition files will always be pulled from the same location. Therefore, you have to put the same winrepo location in each saltenv. Here's how this would look:

file_roots:
  base:
    - C:\ProgramData\Salt Project\Salt\srv\salt\base
    - C:\ProgramData\Salt Project\Salt\srv\salt\winrepo
  test:
    - C:\ProgramData\Salt Project\Salt\srv\salt\test
    - C:\ProgramData\Salt Project\Salt\srv\salt\winrepo
winrepo_source_dir: 'salt://salt-winrepo-ng'
winrepo_dir_ng: C:\ProgramData\Salt Project\Salt\srv\salt\winrepo
winrepo_dir: C:\ProgramData\Salt Project\Salt\srv\salt\winrepo

When you run winrepo.update_git_repos the Git repository will be cloned to the location specified in the winrepo_dir_ng setting. I specified the winrepo_dir setting just so everything gets cloned to the same place. The directory that gets cloned is named salt-winrepo-ng so you specify that in the winrepo_source_dir setting.

The winrepo directory should only contain the package definition files. You wouldn't want to place any states in the winrepo directory as they will be available to both environments.

Master

When working in a Master/Minion environment you have to split up some of the config settings between the master and the minion. Here are some sample configs for winrepo in a Master/Minion environment.

Default Configuration

This is the default configuration if nothing is configured. The config is shown here for clarity. These are the defaults on the master:

file_roots:
  base:
    - /srv/salt
winrepo_dir_ng: /srv/salt/win/repo-ng

This is the default in the minion config:

winrepo_source_dir: 'salt://win/repo-ng'

The winrepo.update_git_repos command will clone the repository to win\repo-ng on the file_roots.

Multiple Salt Environments

To set up multiple saltenvs using a Master/Minion configuration set the following in the master config:

file_roots:
  base:
    - /srv/salt/base
    - /srv/salt/winrepo
  test:
    - /srv/salt/test
    - /srv/salt/winrepo
winrepo_dir_ng: /srv/salt/winrepo
winrepo_dir: /srv/salt/winrepo

Use the winrepo runner to set up the winrepo repository on the master.

salt-run winrepo.update_git_repos

The winrepo will be cloned to /srv/salt/winrepo under a directory named salt-winrepo-ng.

Set the following on the minion config so the minion knows where to find the package definition files in the file_roots:

winrepo_source_dir: 'salt://salt-winrepo-ng'

The same stipulations apply in a Master/Minion configuration as they do in a Masterless configuration

Usage

After completing the configuration and initialization, you can use the Salt package manager commands to manage software on Windows minions.

Note

The following example commands can be run from the master using salt or on a masterless minion using salt-call

Command

Description

1

pkg.list_pkgs

Displays a list of all packages installed in the system.

2

pkg.list_available

Displays the versions available of a particular package to be installed.

3

pkg.install

Installs a given package.

4

pkg.remove

Uninstalls a given package.

List installed packages

Use pkg.list_pkgs to display a list of packages installed on the system.

# From the master
salt -G 'os:windows' pkg.list_pkgs

# From the minion in masterless mode
salt-call --local pkg.list_pkgs

The command displays the software name and the version for every package installed on the system irrespective of whether it was installed by the Salt package manager.

local:
    ----------
    Frhed 1.6.0:
        1.6.0
    GNU Privacy Guard:
        2.2.16
    Gpg4win (3.1.9):
        3.1.9
    git:
        2.17.1.2
    nsis:
        3.03
    python3_x64:
        3.7.4150.0
    salt-minion-py3:
        2019.2.3

The software name indicates whether the software is managed by Salt or not.

If Salt finds a match in the winrepo database, then the software name is the short name as defined in the package definition file. It is usually a single-word, lower-case name.

All other software names are displayed as the full name as shown in Add/Remove Programs. In the above example, Git (git), Nullsoft Installer (nsis), Python 3.7 (python3_x64), and Salt (salt-minion-py3) have corresponding package definition files and are managed by Salt, while Frhed 1.6.0, GNU Privacy guard, and GPG4win are not.

List available versions

Use pkg.list_available to display a list of versions of a package available for installation. You can pass the name of the software in the command. You can refer to the software by its name or its full_name surrounded by quotes.

# From the master
salt winminion pkg.list_available firefox_x64

# From the minion in masterless mode
salt-call --local pkg.list_available firefox_x64

The command lists all versions of Firefox available for installation.

winminion:
    - 69.0
    - 69.0.1
    - 69.0.2
    - 69.0.3
    - 70.0
    - 70.0.1
    - 71.0
    - 72.0
    - 72.0.1
    - 72.0.2
    - 73.0
    - 73.0.1
    - 74.0

Note

For a Linux master, you can surround the file name with single quotes. However, for the cmd shell on Windows, use double quotes when wrapping strings that may contain spaces. Powershell accepts either single quotes or double quotes.

Install a package

Use pkg.install: to install a package.

# From the master
salt winminion pkg.install 'firefox_x64'

# From the minion in masterless mode
salt-call --local pkg.install "firefox_x64"

The command installs the latest version of Firefox.

# From the master
salt winminion pkg.install 'firefox_x64' version=74.0

# From the minion in masterless mode
salt-call --local pkg.install "firefox_x64" version=74.0

The command installs version 74.0 of Firefox.

If a different version of the package is already installed, then the old version is replaced with the version in the winrepo (only if the package supports live updating).

You can also specify the full name of the software while installing:

# From the master
salt winminion pkg.install 'Mozilla Firefox 17.0.1 (x86 en-US)'

# From the minion in masterless mode
salt-call --local pkg.install "Mozilla Firefox 17.0.1 (x86 en-US)"

Remove a package

Use pkg.remove to remove a package.

# From the master
salt winminion pkg.remove firefox_x64

# From the minion in masterless mode
salt-call --local pkg.remove firefox_x64

Package definition file directory structure and naming

All package definition files are stored in the location configured in the winrepo_dir_ng setting. All files in this directory with a .sls file extension are considered package definition files. These files are evaluated to create the metadata file on the minion.

You can maintain standalone package definition files that point to software on other servers or on the internet. In this case the file name is the short name of the software with the .sls extension, for example,``firefox.sls``.

You can also store the binaries for your software together with their software definition files in their own directory. In this scenario, the directory name is the short name for the software and the package definition file stored that directory is named init.sls.

Look at the following example directory structure on a Linux master assuming default config settings:

srv/
|---salt/
|   |---win/
|   |   |---repo-ng/
|   |   |   |---custom_defs/
|   |   |   |   |---ms_office_2013_x64/
|   |   |   |   |   |---access.en-us/
|   |   |   |   |   |---excel.en-us/
|   |   |   |   |   |---outlook.en-us/
|   |   |   |   |   |---powerpoint.en-us/
|   |   |   |   |   |---word.en-us/
|   |   |   |   |   |---init.sls
|   |   |   |   |   |---setup.dll
|   |   |   |   |   |---setup.exe
|   |   |   |   |---openssl.sls
|   |   |   |   |---zoom.sls
|   |   |   |---salt-winrepo-ng/
|   |   |   |   |---auditbeat/
|   |   |   |   |   |---init.sls
|   |   |   |   |   |---install.cmd
|   |   |   |   |   |---install.ps1
|   |   |   |   |   |---remove.cmd
|   |   |   |   |---gpg4win/
|   |   |   |   |   |---init.sls
|   |   |   |   |   |---silent.ini
|   |   |   |   |---7zip.sls
|   |   |   |   |---adobereader.sls
|   |   |   |   |---audacity.sls
|   |   |   |   |---ccleaner.sls
|   |   |   |   |---chrome.sls
|   |   |   |   |---firefox.sls

In the above directory structure:

  • The custom_defs directory contains the following custom package definition files.

    • A folder for MS Office 2013 that contains the installer files for all the MS Office software and a package definition file named init.sls.

    • Two additional standalone package definition files openssl.sls and zoom.sls to install OpenSSl and Zoom.

  • The salt-winrepo-ng directory contains the clone of the git repo specified by the winrepo_remotes_ng config setting.

Warning

Do not modify the files in the salt-winrepo-ng directory as it breaks future runs of winrepo.update_git_repos.

Warning

Do not place any custom software definition files in the salt-winrepo-ng directory as the winrepo.update_git_repos command wipes out the contents of the salt-winrepo-ng directory each time it is run and any extra files stored in the Salt winrepo are lost.

Writing package definition files

You can write your own software definition file if you know:

  • The full name of the software as shown in Add/Remove Programs

  • The exact version number as shown in Add/Remove Programs

  • How to install your software silently from the command line

Here is a YAML software definition file for Firefox:

firefox_x64:
  '74.0':
    full_name: Mozilla Firefox 74.0 (x64 en-US)
    installer: 'https://download-installer.cdn.mozilla.net/pub/firefox/releases/74.0/win64/en-US/Firefox%20Setup%2074.0.exe'
    install_flags: '/S'
    uninstaller: '%ProgramFiles(x86)%/Mozilla Firefox/uninstall/helper.exe'
    uninstall_flags: '/S'
  '73.0.1':
    full_name: Mozilla Firefox 73.0.1 (x64 en-US)
    installer: 'https://download-installer.cdn.mozilla.net/pub/firefox/releases/73.0.1/win64/en-US/Firefox%20Setup%2073.0.1.exe'
    install_flags: '/S'
    uninstaller: '%ProgramFiles(x86)%/Mozilla Firefox/uninstall/helper.exe'
    uninstall_flags: '/S'

The package definition file itself is a data structure written in YAML with three indentation levels:

  • The first level item is a short name that Salt uses to reference the software. This short name is used to install and remove the software and it must be unique across all package definition files in the repo. Also, there must be only one short name in the file.

  • The second level item is the version number. There can be multiple version numbers for a package but they must be unique within the file.

Note

When running pkg.list_pkgs, the short name and version number are displayed when Salt finds a match in the repo. Otherwise, the full package name is displayed.

  • The third indentation level contains all parameters that Salt needs to install the software. The parameters are:

    • full_name : The full name as displayed in Add/Remove Programs

    • installer : The location of the installer binary

    • install_flags : The flags required to install silently

    • uninstaller : The location of the uninstaller binary

    • uninstall_flags : The flags required to uninstall silently

    • msiexec : Use msiexec to install this package

    • allusers : If this is an MSI, install to all users

    • cache_dir : Cache the entire directory in the installer URL if it starts with salt://

    • cache_file : Cache a single file in the installer URL if it starts with salt://

    • use_scheduler : Launch the installer using the task scheduler

    • source_hash : The hash sum for the installer

Example package definition files

This section provides some examples of package definition files for different use cases such as:

These examples enable you to gain a better understanding of the usage of different file parameters. To understand the examples, you need a basic Understanding Jinja. For an exhaustive dive into Jinja, refer to the official Jinja Template Designer documentation.

Example: Simple

Here is a pure YAML example of a simple package definition file for Firefox:

firefox_x64:
  '74.0':
    full_name: Mozilla Firefox 74.0 (x64 en-US)
    installer: 'https://download-installer.cdn.mozilla.net/pub/firefox/releases/74.0/win64/en-US/Firefox%20Setup%2074.0.exe'
    install_flags: '/S'
    uninstaller: '%ProgramFiles(x86)%/Mozilla Firefox/uninstall/helper.exe'
    uninstall_flags: '/S'
  '73.0.1':
    full_name: Mozilla Firefox 73.0.1 (x64 en-US)
    installer: 'https://download-installer.cdn.mozilla.net/pub/firefox/releases/73.0.1/win64/en-US/Firefox%20Setup%2073.0.1.exe'
    install_flags: '/S'
    uninstaller: '%ProgramFiles(x86)%/Mozilla Firefox/uninstall/helper.exe'
    uninstall_flags: '/S'

The first line is the short name of the software which is firefox_x64.

Important

The short name must be unique across all other short names in the software repository. The full_name combined with the version must also be unique.

The second line is the software version and is indented two spaces.

Important

The version number must be enclosed in quotes or the YAML parser removes the trailing zeros. For example, if the version number 74.0 is not enclosed within quotes, then the version number is rendered as 74.

The lines following the version are indented two more spaces and contain all the information needed to install the Firefox package.

Important

You can specify multiple versions of software by specifying multiple version numbers at the same indentation level as the first with its software definition below it.

Important

The full_name must match exactly what is shown in Add/Remove Programs (appwiz.cpl)

Example: JINJA templated package definition file

JINJA is the default templating language used in package definition files. You can use JINJA to add variables and expressions to package definition files that get replaced with values when the .sls go through the Salt renderer.

When there are tens or hundreds of versions available for a piece of software, the definition file can become large and cumbersome to maintain. In this scenario, JINJA can be used to add logic, variables, and expressions to automatically create the package definition file for software with multiple versions.

Here is a an example of a package definition file for Firefox that uses JINJA:

{%- set lang = salt['config.get']('firefox:pkg:lang', 'en-US') %}

firefox_x64:
  {% for version in ['74.0',
                     '73.0.1', '73.0',
                     '72.0.2', '72.0.1', '72.0',
                     '71.0', '70.0.1', '70.0',
                     '69.0.3', '69.0.2', '69.0.1'] %}
  '{{ version }}':
    full_name: 'Mozilla Firefox {{ version }} (x64 {{ lang }})'
    installer: 'https://download-installer.cdn.mozilla.net/pub/firefox/releases/{{ version }}/win64/{{ lang }}/Firefox%20Setup%20{{ version }}.exe'
    install_flags: '/S'
    uninstaller: '%ProgramFiles%\Mozilla Firefox\uninstall\helper.exe'
    uninstall_flags: '/S'
  {% endfor %}

In this example, JINJA is used to generate a package definition file that defines how to install 12 versions of Firefox. Jinja is used to create a list of available versions. The list is iterated through a for loop where each version is placed in the version variable. The version is inserted everywhere there is a {{ version }} marker inside the for loop.

The single variable (lang) defined at the top of the package definition identifies the language of the package. You can access the Salt modules using the salt keyword. In this case, the config.get function is invoked to retrieve the language setting. If the lang variable is not defined then the default value is en-US.

Example: Package definition file to install the latest version

Some software vendors do not provide access to all versions of their software. Instead, they provide a single URL to what is always the latest version. In some cases, the software keeps itself up to date. One example of this is the Google Chrome web browser.

To handle situations such as these, set the version to latest. Here's an example of a package definition file to install the latest version of Chrome.

chrome:
  latest:
    full_name: 'Google Chrome'
    installer: 'https://dl.google.com/edgedl/chrome/install/GoogleChromeStandaloneEnterprise.msi'
    install_flags: '/qn /norestart'
    uninstaller: 'https://dl.google.com/edgedl/chrome/install/GoogleChromeStandaloneEnterprise.msi'
    uninstall_flags: '/qn /norestart'
    msiexec: True

In the above example:

  • Version is set to latest. Salt then installs the latest version of Chrome at the URL and displays that version.

  • msiexec is set to True, hence the software is installed using an MSI.

Example: Package definition file to install an MSI patch

For MSI installers, when the msiexec parameter is set to true, the /i option is used for installation, and the /x option is used for uninstallation. However, when installing an MSI patch, the /i and /x options cannot be combined.

Here is an example of a package definition file to install an MSI patch:

MyApp:
  '1.0':
    full_name: MyApp
    installer: 'salt://win/repo-ng/MyApp/MyApp.1.0.msi'
    install_flags: '/qn /norestart'
    uninstaller: '{B5B5868F-23BA-297A-917D-0DF345TF5764}'
    uninstall_flags: '/qn /norestart'
    msiexec: True
  '1.1':
    full_name: MyApp
    installer: 'salt://win/repo-ng/MyApp/MyApp.1.0.msi'
    install_flags: '/qn /norestart /update "%cd%\\MyApp.1.1.msp" '
    uninstaller: '{B5B5868F-23BA-297A-917D-0DF345TF5764}'
    uninstall_flags: '/qn /norestart'
    msiexec: True
    cache_file: salt://win/repo-ng/MyApp/MyApp.1.1.msp

In the above example:

  • Version 1.0 of the software installs the application using the 1.0 MSI defined in the installer parameter.

  • There is no file to be cached and the install_flags parameter does not include any special values.

Version 1.1 of the software uses the same installer file as Version 1.0. Now, to apply a patch to Version 1.0, make the following changes in the package definition file:

  • Place the patch file (MSP file) in the same directory as the installer file (MSI file) on the file_roots

  • In the cache_file parameter, specify the path to the single patch file.

  • In the install_flags parameter, add the /update flag and include the path to the MSP file using the %cd% environment variable. %cd% resolves to the current working directory, which is the location in the minion cache where the installer file is cached.

For more information, see issue #32780.

The same approach could be used for applying MST files for MSIs and answer files for other types of .exe-based installers.

Parameters

This section describes the parameters placed under the version in the package definition file. Examples can be found on the Salt winrepo repository.

full_name (str)

The full name of the software as shown in "Add/Remove Programs". You can also retrieve the full name of the package by installing the package manually and then running pkg.list_pkgs. Here's an example of the output from pkg.list_pkgs:

salt 'test-2008' pkg.list_pkgs
test-2008
    ----------
    7-Zip 9.20 (x64 edition):
        9.20.00.0
    Mozilla Firefox 74.0 (x64 en-US)
        74.0
    Mozilla Maintenance Service:
        74.0
    salt-minion-py3:
        3001

Notice the full Name for Firefox: Mozilla Firefox 74.0 (x64 en-US). The full_name parameter in the package definition file must match this name.

The example below shows the pkg.list_pkgs for a machine that has Mozilla Firefox 74.0 installed with a package definition file for that version of Firefox.

test-2008:
    ----------
    7zip:
        9.20.00.0
    Mozilla Maintenance Service:
        74.0
    firefox_x64:
        74.0
    salt-minion-py3:
        3001

On running pkg.list_pkgs, if any of the software installed on the machine matches the full name defined in any one of the software definition files in the repository, then the package name is displayed in the output.

Important

The version number and full_name must match the output of pkg.list_pkgs so that the installation status can be verified by the state system.

Note

You can successfully install packages using pkg.install, even if the full_name or the version number doesn't match. The module will complete successfully, but continue to display the full name in pkg.list_pkgs. If this is happening, verify that the full_name and the version match exactly what is displayed in Add/Remove Programs.

Tip

To force Salt to display the full name when there's already an existing package definition file on the system, you can pass a bogus saltenv parameter to the command like so: pkg.list_pkgs saltenv=NotARealEnv

Tip

It's important use pkg.refresh_db to check for errors and ensure the latest package definition is on any minion you're testing new definitions on.

installer (str)

The path to the binary (.exe, .msi) that installs the package.

This can be a local path or a URL. If it is a URL or a Salt path (salt://), then the package is cached locally and then executed. If it is a path to a file on disk or a file share, then it is executed directly.

Note

When storing software in the same location as the winrepo:

  • Create a sub folder named after the package.

  • Store the package definition file named init.sls and the binary installer in the same sub folder if you are hosting those files on the file_roots.

Note

The pkg.refresh_db command processes all .sls files in all sub directories in the winrepo_dir_ng directory.

install_flags (str)

The flags passed to the installer for silent installation.

You may be able to find these flags by adding /? or /h when running the installer from the command line. See WPKG project wiki for information on silent install flags.

Warning

Always ensure that the installer has the ability to install silently, otherwise Salt appears to hang while the installer waits for user input.

uninstaller (str)

The path to the program to uninstall the software.

This can be the path to the same .exe or .msi used to install the software. If you use a .msi to install the software, then you can either use the GUID of the software or the same .msi to uninstall the software.

You can find the uninstall information in the registry:

  • Software\Microsoft\Windows\CurrentVersion\Uninstall

  • Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall

Here's an example that uses the GUID to uninstall software:

7zip:
  '9.20.00.0':
    full_name: 7-Zip 9.20 (x64 edition)
    installer: salt://win/repo-ng/7zip/7z920-x64.msi
    install_flags: '/qn /norestart'
    uninstaller: '{23170F69-40C1-2702-0920-000001000000}'
    uninstall_flags: '/qn /norestart'
    msiexec: True

Here's an example that uses the MSI installer to uninstall software:

7zip:
  '9.20.00.0':
    full_name: 7-Zip 9.20 (x64 edition)
    installer: salt://win/repo-ng/7zip/7z920-x64.msi
    install_flags: '/qn /norestart'
    uninstaller: salt://win/repo-ng/7zip/7z920-x64.msi
    uninstall_flags: '/qn /norestart'
    msiexec: True

uninstall_flags (str)

The flags passed to the uninstaller for silent uninstallation.

You may be able to find these flags by adding /? or /h when running the uninstaller from the command-line. See WPKG project wiki for information on silent uninstall flags.

Warning

Always ensure that the installer has the ability to uninstall silently, otherwise Salt appears to hang while the uninstaller waits for user input.

msiexec (bool, str)

This setting informs Salt to use msiexec /i to install the package and msiexec /x to uninstall. This setting only applies to .msi installations.

Possible options are:

  • True

  • False (default)

  • the path to msiexec.exe on your system

7zip:
  '9.20.00.0':
    full_name: 7-Zip 9.20 (x64 edition)
    installer: salt://win/repo/7zip/7z920-x64.msi
    install_flags: '/qn /norestart'
    uninstaller: salt://win/repo/7zip/7z920-x64.msi
    uninstall_flags: '/qn /norestart'
    msiexec: 'C:\Windows\System32\msiexec.exe'

allusers (bool)

This parameter is specific to .msi installations. It tells msiexec to install the software for all users. The default is True.

cache_dir (bool)

This setting requires the software to be stored on the file_roots and only applies to URLs that begin with salt://. If set to True, then the entire directory where the installer resides is recursively cached. This is useful for installers that depend on other files in the same directory for installation.

Warning

If set to True, then all files and directories in the same location as the installer file are copied down to the minion. For example, if you place your package definition file with cache_dir: True in the root of winrepo (/srv/salt/win/repo-ng) then the entire contents of winrepo is cached to the minion. Therefore, it is best practice to place your package definition file along with its installer files in a subdirectory if they are stored in winrepo.

Here's an example using cache_dir:

sqlexpress:
  '12.0.2000.8':
    full_name: Microsoft SQL Server 2014 Setup (English)
    installer: 'salt://win/repo/sqlexpress/setup.exe'
    install_flags: '/ACTION=install /IACCEPTSQLSERVERLICENSETERMS /Q'
    cache_dir: True

cache_file (str)

This setting requires the file to be stored on the file_roots and only applies to URLs that begin with salt://. It indicates that the single file specified is copied down for use with the installer. It is copied to the same location as the installer. Use this setting instead of cache_dir when you only need to cache a single file.

use_scheduler (bool)

If set to True, Windows uses the task scheduler to run the installation. A one-time task is created in the task scheduler and launched. The return to the minion is that the task was launched successfully, not that the software was installed successfully.

Note

This is used in the package definition for Salt itself. The first thing the Salt installer does is kill the Salt service, which then kills all child processes. If the Salt installer is launched via Salt, then the installer is killed with the salt-minion service, leaving Salt on the machine but not running. Using the task scheduler allows an external process to launch the Salt installer so its processes aren't killed when the Salt service is stopped.

source_hash (str)

This setting informs Salt to compare a hash sum of the installer to the provided hash sum before execution. The value can be formatted as <hash_algorithm>=<hash_sum>, or it can be a URI to a file containing the hash sum.

For a list of supported algorithms, see the hashlib documentation.

Here's an example using source_hash:

messageanalyzer:
  '4.0.7551.0':
    full_name: 'Microsoft Message Analyzer'
    installer: 'salt://win/repo/messageanalyzer/MessageAnalyzer64.msi'
    install_flags: '/quiet /norestart'
    uninstaller: '{1CC02C23-8FCD-487E-860C-311EC0A0C933}'
    uninstall_flags: '/quiet /norestart'
    msiexec: True
    source_hash: 'sha1=62875ff451f13b10a8ff988f2943e76a4735d3d4'

Not Implemented

The following parameters are often seen in the software definition files hosted on the Git repo. However, they are not implemented and do not affect the installation process.

param bool reboot:

Not implemented

param str locale:

Not implemented

Managing Windows Software on a Standalone Windows Minion

The Windows Software Repository functions similarly in a standalone environment, with a few differences in the configuration.

To replace the winrepo runner used on the Salt master, an execution module exists to provide the same functionality to standalone minions. The functions for the module share the same names with functions in the runner and are used in the same way; the only difference is that salt-call is used instead of salt-run to run those functions:

salt-call winrepo.update_git_repos
salt-call pkg.refresh_db

After executing the previous commands, the repository on the standalone system is ready for use.

Troubleshooting

My software installs correctly but pkg.installed says it failed

If you have a package that seems to install properly but Salt reports a failure then it is likely you have a version or full_name mismatch.

  • Check the full_name and version of the package as shown in Add/Remove Programs (appwiz.cpl).

  • Use pkg.list_pkgs to check that the full_name and version exactly match what is installed.

  • Verify that the full_name and version in the package definition file match the full name and version in Add/Remove programs.

  • Ensure that the version is wrapped in single quotes in the package definition file.

Changes to package definition files not being picked up

Make sure you refresh the database on the minion (pkg.refresh_db) after updating package definition files in the repo.

salt winminion pkg.refresh_db

Winrepo upgrade issues

To minimize potential issues, it is a good idea to remove any winrepo git repositories that were checked out by the legacy (pre-2015.8.0) winrepo code when upgrading the master to 2015.8.0 or later. Run winrepo.update_git_repos to clone them anew after the master is started.

pygit2 / GitPython Support for Maintaining Git Repos

pygit2 and GitPython are the supported python interfaces to Git. The runner winrepo.update_git_repos uses the same underlying code as Git Fileserver Backend and Git External Pillar to maintain and update its local clones of git repositories.

Note

If compatible versions of both pygit2 and GitPython are installed, then Salt will prefer pygit2. To override this behavior use the winrepo_provider configuration parameter, ie: winrepo_provider: gitpython

Accessing authenticated Git repos (pygit2)

pygit2 enables you to access authenticated git repositories and set per-remote config settings. An example of this is:

winrepo_remotes:
  - https://github.com/saltstack/salt-winrepo.git
  - git@github.com:myuser/myrepo.git:
    - pubkey: /path/to/key.pub
    - privkey: /path/to/key
    - passphrase: myaw3s0m3pa$$phr4$3
  - https://github.com/myuser/privaterepo.git:
    - user: mygithubuser
    - password: CorrectHorseBatteryStaple

Note

The per-remote configuration settings work in the same manner as they do in gitfs, with global parameters being overridden by their per-remote counterparts. For instance, setting winrepo_passphrase sets a global passphrase for winrepo that applies to all SSH-based remotes, unless overridden by a passphrase per-remote parameter.

See here for a detailed explanation of how per-remote configuration works in gitfs. The same principles apply to winrepo.

Maintaining Git repos

A clean argument is added to the winrepo.update_git_repos runner to maintain the Git repos. When clean=True the runner removes directories under the winrepo_dir_ng/winrepo_dir_ng that are not explicitly configured. This eliminates the need to manually remove these directories when a repo is removed from the config file.

salt-run winrepo.update_git_repos clean=True

If a mix of git and non-git Windows Repo definition files are used, then do not pass clean=True, as it removes the directories containing non-git definitions.

Name collisions between repos

Salt detects collisions between repository names. The winrepo.update_git_repos runner does not execute successfully if any collisions between repository names are detected. Consider the following configuration:

winrepo_remotes:
  - https://foo.com/bar/baz.git
  - https://mydomain.tld/baz.git
  - https://github.com/foobar/baz.git

With the above configuration, the winrepo.update_git_repos runner fails to execute as all three repos would be checked out to the same directory. To resolve this conflict, use the per-remote parameter called name.

winrepo_remotes:
  - https://foo.com/bar/baz.git
  - https://mydomain.tld/baz.git:
    - name: baz_junior
  - https://github.com/foobar/baz.git:
    - name: baz_the_third

Now on running the winrepo.update_git_repos: