saltstack / saltstack/salt

[Bug]: `HAS_WINRM` always `False` in salt-cloud due to wrong distribution name in `importlib.metadata` lookup

Open Beginner friendly
#69,976 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Python
Stars
15.7k
Forks
5.6k
Avg merge
2d 44m
Merged PRs (30d)
80

Description

What happened?

Salt Version: 3006.27 (Sulfur)
Component: salt/utils/cloud.py
Affected feature: salt-cloud Windows deployment via WinRM (use_winrm: true)


Description

When deploying Windows VMs via salt-cloud with use_winrm: true, the deployment
fails with the following error regardless of whether pywinrm is installed:

[ERROR ] WinRM requested but module winrm could not be imported.
Ensure you are using version 0.3.0 or higher.
[ERROR ] Failed to start Salt on host <hostname>

The root cause is an incorrect distribution name passed to
importlib.metadata.version() in the HAS_WINRM detection block of
salt/utils/cloud.py. The code looks up the distribution name "winrm" but
the legitimate PyPI package is distributed under the name "pywinrm". These
are different distribution names. importlib.metadata.version("winrm") raises
PackageNotFoundError (a subclass of ImportError), which is silently caught
by the except ImportError block, setting HAS_WINRM = False before the
actual import winrm line is ever reached — even when pywinrm is correctly
installed and fully importable.


Affected Code

File: salt/utils/cloud.py
Approximate line: 83–95

# Set the minimum version of PyWinrm.
WINRM_MIN_VER = "0.3.0"
try:
    import importlib
    import importlib.metadata
    # Verify WinRM 0.3.0 or greater
    version = importlib.metadata.version("winrm")   # ← BUG: wrong name
    if not salt.utils.versions.compare(version, ">=", WINRM_MIN_VER):
        HAS_WINRM = False
    else:
        HAS_WINRM = True
    import winrm
    from winrm.exceptions import WinRMTransportError
except ImportError:
    HAS_WINRM = False

Steps to Reproduce

  1. Install Salt 3006.27 onedir on a Linux salt-master.
  2. Install pywinrm into the Salt onedir Python environment:
   sudo /opt/saltstack/salt/bin/pip install "pywinrm>=0.3.0"
  1. Confirm pywinrm is installed and importable:
   sudo /opt/saltstack/salt/bin/python3.11 -c "import winrm; print(winrm.__version__)"
   # Output: 0.5.0
  1. Confirm all sub-imports succeed:
   sudo /opt/saltstack/salt/bin/python3.11 -c "
   import winrm
   from winrm.exceptions import WinRMTransportError
   from winrm.protocol import Protocol
   print('all imports OK')
   "
   # Output: all imports OK
  1. Check HAS_WINRM in the salt module context:
   sudo /opt/saltstack/salt/bin/python3.11 -c "
   import salt.utils.cloud
   print('HAS_WINRM:', salt.utils.cloud.HAS_WINRM)
   "
   # Output: HAS_WINRM: False
  1. Attempt a salt-cloud Windows VM deployment with use_winrm: true in the
    cloud profile. Deployment fails with the error shown above.

Root Cause Diagnosis

The importlib.metadata.version() call uses the wrong distribution name.
The PyPI distribution name is pywinrm; the importable module name is winrm.
These are not the same string.

sudo /opt/saltstack/salt/bin/python3.11 -c "
import importlib.metadata
try:
    v = importlib.metadata.version('winrm')
    print('found as winrm:', v)
except Exception as e:
    print('not found as winrm:', type(e).__name__, e)
try:
    v = importlib.metadata.version('pywinrm')
    print('found as pywinrm:', v)
except Exception as e:
    print('not found as pywinrm:', type(e).__name__, e)
"

Output:

not found as winrm: PackageNotFoundError No package metadata was found for winrm
found as pywinrm: 0.5.0

PackageNotFoundError is a subclass of ModuleNotFoundErrorImportError,
so it is silently caught by the except ImportError block. HAS_WINRM is set
to False and the actual import winrm statement that would have succeeded
is never reached.

Note: There is a separate spam/clone package on PyPI literally named winrm
(reported in https://github.com/diyan/pywinrm/issues/319) which is not the
legitimate pywinrm package. The importlib.metadata.version("winrm") lookup
is effectively checking for this spam package rather than the real one.


Expected Behavior

HAS_WINRM should be True when pywinrm >= 0.3.0 is installed and
importable. salt-cloud Windows deployments with use_winrm: true should
proceed normally.

Actual Behavior

HAS_WINRM is always False when pywinrm is installed via pip into the
onedir environment, because importlib.metadata.version("winrm") raises
PackageNotFoundError before the actual import is attempted.


Fix

Change the distribution name in the importlib.metadata.version() call from
"winrm" to "pywinrm":

# Before (incorrect)
version = importlib.metadata.version("winrm")

# After (correct)
version = importlib.metadata.version("pywinrm")

This is consistent with how the package has always been referenced in Salt's
own requirements files and documentation (pip install pywinrm).


Workaround

Until a fix is released, apply the following patch after each salt-master
update:

sudo sed -i \
  's/importlib\.metadata\.version("winrm")/importlib.metadata.version("pywinrm")/' \
  /opt/saltstack/salt/lib/python3.11/site-packages/salt/utils/cloud.py

# Verify
grep -n "importlib.metadata.version" \
  /opt/saltstack/salt/lib/python3.11/site-packages/salt/utils/cloud.py

# Confirm fix
sudo /opt/saltstack/salt/bin/python3.11 -c "
import salt.utils.cloud
print('HAS_WINRM:', salt.utils.cloud.HAS_WINRM)
"
# Expected: HAS_WINRM: True

sudo systemctl restart salt-master

Related

Type of salt install

Official rpm

Major version

3006.x

What supported OS are you seeing the problem on? Can select multiple. (If bug appears on an unsupported OS, please open a GitHub Discussion instead)

windows-2025

salt --versions-report output
$ sudo salt --versions-report
Salt Version:
          Salt: 3006.27

Python Version:
        Python: 3.11.15 (main, Jun 29 2026, 22:21:49) [GCC 11.2.0]

Dependency Versions:
          cffi: 2.0.0
      cherrypy: 18.10.0
  cryptography: 47.0.0
      dateutil: 2.9.0.post0
     docker-py: Not Installed
         gitdb: 4.0.12
     gitpython: 3.1.50
        Jinja2: 3.1.6
       libgit2: Not Installed
  looseversion: 1.3.0
      M2Crypto: Not Installed
          Mako: Not Installed
       msgpack: 1.1.2
  msgpack-pure: Not Installed
  mysql-python: Not Installed
     packaging: 24.0
     pycparser: 3.00
      pycrypto: Not Installed
  pycryptodome: 3.23.0
        pygit2: Not Installed
  python-gnupg: 0.5.6
        PyYAML: 6.0.3
         PyZMQ: 27.1.0
        relenv: 0.22.16
         smmap: 5.0.2
       timelib: 0.3.0
       Tornado: 6.5.5
           ZMQ: 4.3.5

System Versions:
          dist: rhel 9.8 Plow
        locale: utf-8
       machine: x86_64
       release: 5.14.0-687.36.1.el9_8.x86_64
        system: Linux
       version: Red Hat Enterprise Linux 9.8 Plow

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in salt/utils/cloud.py around the HAS_WINRM detection block and inspect how importlib.metadata.version() checks the WinRM dependency. Reproduce the lookup and import checks in the issue using the Salt onedir Python environment. Done means HAS_WINRM is true with pywinrm installed and the existing salt-cloud WinRM deployment path can proceed.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
cloud
Issue type
Bug
Difficulty
1/5
Estimated time
Under an hour
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.