Error: Update package (1603)

Questions about WAPT Packaging / Requests and help regarding Wapt packages.
Forum Rules
Community Forum Rules
* English support on www.reddit.com/r/wapt
* French community support is available on this forum
* Please prefix the topic title with [RESOLVED] if it is resolved.
* Please do not edit a topic that is tagged [RESOLVED]. Open a new topic referencing the old one.
* Specify the installed WAPT version, full version, and build number (2.2.1.11957 / 2.2.2.12337 / etc.) as well as the Enterprise/Discovery edition.
* Versions 1.8.2 and earlier are no longer supported. The only questions accepted regarding version 1.8.2 are related to upgrading to a supported version (2.1, 2.2, etc.).
* Specify the server OS (Linux/Windows) and version (Debian Buster/Bullseye - CentOS 7 - Windows Server 2012/2016/2019).
* Specify the OS of the administration/package creation machine and the machine with the problematic agent, if applicable (Windows 7/10/11/Debian 11/etc.).
* Avoid asking multiple questions when opening a topic, otherwise it may be ignored. If there are multiple topics, open separate topics, preferably one after the other and not all at the same time (i.e., do not spam the forum).
* Include code snippets, screenshots, and other images directly in the post. Links to Pastebin, Bitly, and other third-party sites will be systematically removed.
* As with any community forum, support is provided voluntarily by members. If you require commercial support, you can contact Tranquil IT's sales department at 02.40.97.57.55
ddcorazon
Messages: 37
Registration: May 30, 2023 - 11:53 p.m.

August 20, 2025 - 00:41

(following)

Once finished, if everything went well, we'll see this in the browser-msi-after-fix audit
Screenshot 2025-08-20 001828.png
Screenshot 2025-08-20 001828.png (42.03 KB) Viewed 11656 times
And normally, once it's finished, I manage to do my upgrades from Wapt
Screenshot 2025-08-20 003943.png
Screenshot 2025-08-20 003943.png (58.23 KB) Viewed 11656 times
I have a few machines where it didn't work (it doesn't work directly on the machines either, even with the correct MSI of the correct version)

Below is the code (it might be more optimized, I admit)😅)

Code: Select all

#Auteur ddcorazon
# -*- coding: utf-8 -*-
from setuphelpers import *
import os, glob, shutil, traceback

# === CONFIG ===
tools = ["chrome", "edge"]
AUDIT_SCOPE_BEFORE = "browser-msi"
AUDIT_SCOPE_AFTER  = "browser-msi-after-fix"

_UNINSTALL_KEYS = [
    r'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall',
    r'HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall',
    r'HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall',
    r'HKCU\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall',
]

def install():
    """Copie le dossier 'msi' du paquet vers persistent_dir\\msi pour l'audit."""
    src_dir = makepath(os.getcwd(), "msi")  # côté build/exec, cwd = racine du paquet
    dst_dir = makepath(persistent_dir, "msi")
    if os.path.isdir(src_dir):
        print("[INSTALL] Sync MSIs ->", dst_dir)
        if os.path.isdir(dst_dir):
            shutil.rmtree(dst_dir, ignore_errors=True)
        shutil.copytree(src_dir, dst_dir)
    else:
        print("[INSTALL] Aucun dossier 'msi' à copier (", src_dir, ")")

def uninstall():
    pass

# ----------------- Helpers -----------------
def _normalize_tools(t):
    if isinstance(t, (list, tuple)):
        lst = [str(x).strip() for x in t]
    else:
        lst = [s.strip() for s in str(t).split(",")]
    return [s.lower() for s in lst if s]

def _scan_registry(term):
    """Retourne (DisplayName, LocalPackagePath, DisplayVersion)."""
    for base in _UNINSTALL_KEYS:
        try:
            for sub in glob.glob(base + r'\*'):
                try:
                    dn = registry_readstring(sub, 'DisplayName', '')
                    if not dn or term not in dn.lower():
                        continue
                    lp = registry_readstring(sub, 'LocalPackage', '')
                    dv = registry_readstring(sub, 'DisplayVersion', '')
                    return ((dn or '').strip(), (lp or '').strip(), (dv or '').strip())
                except Exception:
                    continue
        except Exception:
            continue
    return (None, None, None)

def _scan_wmi(term):
    """Fallback WMI (Win32_Product)."""
    try:
        ps = (
            r"Get-WmiObject -Class Win32_Product | "
            r"Where-Object { $_.Name -ne $null -and $_.Name -like '*" + term + r"*' } | "
            r"Select-Object Name, LocalPackage, Version"
        )
        data = run_powershell(ps)
        if not data:
            return (None, None, None)
        row = data[0] if isinstance(data, list) else data
        name  = (row.get('Name') or '').strip()
        local = (row.get('LocalPackage') or '').strip()
        ver   = (row.get('Version') or '').strip()
        return (name or None, local or None, ver or None)
    except Exception:
        return (None, None, None)

def _detect_one(term):
    """Retourne (name, pkg_basename, version, file_flag)."""
    name, local, ver = _scan_registry(term)
    if not name and not local and not ver:
        name, local, ver = _scan_wmi(term)

    pkg = os.path.basename(local) if local else ""
    file_flag = "Unknown"
    if pkg:
        installer_dir = os.path.join(os.environ.get("SystemRoot", r"C:\Windows"), "Installer")
        candidate1 = os.path.join(installer_dir, pkg)
        candidate2 = local
        if (candidate2 and os.path.exists(candidate2)) or os.path.exists(candidate1):
            file_flag = "Found"
        else:
            file_flag = "Missing"

    return name or "", pkg, ver or "", file_flag

def _run_audit(scope):
    """Exécute l’audit pour tous les tools, retourne (result_data, found_any)."""
    result_data = {}
    found_any = False

    for term in _normalize_tools(tools):
        section = term.capitalize()
        name, pkg, ver, file_flag = _detect_one(term)

        result_data[section] = {
            "Name": name,
            "Package": pkg,
            "Version": ver,
            "File": file_flag
        }
        if name or pkg or ver:
            found_any = True

    WAPT.write_audit_data_if_changed(scope, "audit_data", result_data)
    WAPT.write_audit_data_if_changed(scope, "result", "OK" if found_any else "NotFound")
    return result_data, found_any

def _fix_missing(result_data):
    """
    Copie les MSI manquants depuis persistent_dir\\msi\\<tool>-<version>.msi
    -> C:\\Windows\\Installer\\<package>
    """
    installer_dir = os.path.join(os.environ.get("SystemRoot", r"C:\Windows"), "Installer")
    src_dir = makepath(persistent_dir, "msi")
    print(f"[INFO] Dossier source MSI: {src_dir}")

    for app, info in result_data.items():
        tool = app.lower()
        pkg  = info.get("Package")   # ex: 16af95e8.msi
        ver  = info.get("Version")   # ex: 139.0.x.x
        flag = info.get("File")

        if flag == "Missing" and pkg and ver:
            src = makepath(src_dir, f"{tool}-{ver}.msi")
            dst = makepath(installer_dir, pkg)
            if os.path.exists(src):
                print(f"[FIX] Copie {src} -> {dst}")
                os.makedirs(installer_dir, exist_ok=True)
                shutil.copyfile(src, dst)
            else:
                print(f"[WARN] MSI source introuvable pour {app}: {src}")

def _cleanup_persistent_msis():
    """Supprime persistent_dir\\msi pour ne rien laisser sur le poste."""
    msi_dir = makepath(persistent_dir, "msi")
    if os.path.isdir(msi_dir):
        print(f"[CLEANUP] Suppression du répertoire: {msi_dir}")
        shutil.rmtree(msi_dir, ignore_errors=True)

# ----------------- Audit principal -----------------
def audit():
    try:
        # 1) Audit avant
        before_data, found_any = _run_audit(AUDIT_SCOPE_BEFORE)

        # 2) Corrections si nécessaires
        if found_any:
            needs_fix = any(info.get("File") == "Missing" for info in before_data.values())
            if needs_fix:
                _fix_missing(before_data)
                # 3) Audit après correction
                _run_audit(AUDIT_SCOPE_AFTER)
            return "OK"
        else:
            return "NotFound"

    except Exception as e:
        print("[ERROR] Exception durant l'audit :", e)
        print(traceback.format_exc())
        return "ERROR"

    finally:
        # 4) Toujours nettoyer le persistent_dir\msi en fin d’audit
        _cleanup_persistent_msis()
 
ddcorazon
Messages: 37
Registration: May 30, 2023 - 11:53 p.m.

September 3, 2025 - 3:30 PM

Hello everyone,

Given the near impossibility of finding old Chrome Enterprise MSI files, I opted for another solution (which incidentally works for Edge and "any other MSI")
It's a rather drastic solution, but if we have no other choice, the idea is to use an old Microsoft utility called msizap. By retrieving the identifier of the installed MSI, we can remove "all references" to that MSI from the registry. So, for example, Chrome or Edge will no longer appear as installed in the Control Panel, even though they still are.
So here we retrieve the identifier
Get-WmiObject Win32_Product | Where-Object { $_.Name -like "*chrome*" } | Select-Object Name, Version, IdentifyingNumber

Name Version IdentifyingNumber
---- ------- -----------------
Google Chrome 139.0.7258.155 {AC157B81-B63F-3C87-802D-6A050E1EFBE7}

Then, using MSIzap.exe T {the identifier we retrieved}

Once that was done, Windows considered Chrome not to be installed, so we forced the installation from WAPT and it performed a clean install. Once fixed, I was able to complete the subsequent update without any problems.

Below is the code (it can be optimized; there are still some things that aren't used, given the many things I've tried)

Code: Select all

# -*- coding: utf-8 -*-
from setuphelpers import *
import os, glob, shutil, traceback

# === CONFIG ===
tools = ["chrome"]
AUDIT_SCOPE_BEFORE = "chrome-msi"
AUDIT_SCOPE_AFTER  = "chrome-msi-after-fix"

_UNINSTALL_KEYS = [
    r'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall',
    r'HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall',
    r'HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall',
    r'HKCU\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall',
]

def install():
    """Copie msizap.exe dans persistent_dir\\msi pour utilisation locale."""
    src_dir = makepath(os.getcwd(), "msi")  # côté paquet WAPT
    dst_dir = makepath(persistent_dir, "msi")
    if os.path.isdir(src_dir):
        print("[INSTALL] Sync MSIs ->", dst_dir)
        if os.path.isdir(dst_dir):
            shutil.rmtree(dst_dir, ignore_errors=True)
        shutil.copytree(src_dir, dst_dir)
    else:
        print("[INSTALL] Aucun dossier 'msi' à copier (", src_dir, ")")

def uninstall():
    pass

# ----------------- Helpers -----------------
def _normalize_tools(t):
    if isinstance(t, (list, tuple)):
        lst = [str(x).strip() for x in t]
    else:
        lst = [s.strip() for s in str(t).split(",")]
    return [s.lower() for s in lst if s]

def _scan_registry(term):
    """Retourne (DisplayName, LocalPackagePath, DisplayVersion)."""
    for base in _UNINSTALL_KEYS:
        try:
            for sub in glob.glob(base + r'\*'):
                try:
                    dn = registry_readstring(sub, 'DisplayName', '')
                    if not dn or term not in dn.lower():
                        continue
                    lp = registry_readstring(sub, 'LocalPackage', '')
                    dv = registry_readstring(sub, 'DisplayVersion', '')
                    return ((dn or '').strip(), (lp or '').strip(), (dv or '').strip())
                except Exception:
                    continue
        except Exception:
            continue
    return (None, None, None)

def _scan_wmi(term):
    """Retourne (Name, LocalPackage, Version, IdentifyingNumber)."""
    try:
        ps = (
            r"Get-WmiObject -Class Win32_Product | "
            r"Where-Object { $_.Name -ne $null -and $_.Name -like '*" + term + r"*' } | "
            r"Select-Object Name, LocalPackage, Version, IdentifyingNumber"
        )
        data = run_powershell(ps)
        if not data:
            return (None, None, None, None)
        row = data[0] if isinstance(data, list) else data
        name  = (row.get('Name') or '').strip()
        local = (row.get('LocalPackage') or '').strip()
        ver   = (row.get('Version') or '').strip()
        prod  = (row.get('IdentifyingNumber') or '').strip()
        return (name or None, local or None, ver or None, prod or None)
    except Exception:
        return (None, None, None, None)

def _detect_one(term):
    """Retourne (name, pkg_basename, version, file_flag, product_id)."""
    name, local, ver = _scan_registry(term)
    prod_id = ""
    if not name and not local and not ver:
        name, local, ver, prod_id = _scan_wmi(term)
    else:
        # récupère aussi via WMI pour ProductCode
        _, _, _, prod_id = _scan_wmi(term)

    pkg = os.path.basename(local) if local else ""
    file_flag = "Unknown"
    if pkg:
        installer_dir = os.path.join(os.environ.get("SystemRoot", r"C:\Windows"), "Installer")
        candidate1 = os.path.join(installer_dir, pkg)
        candidate2 = local
        if (candidate2 and os.path.exists(candidate2)) or os.path.exists(candidate1):
            file_flag = "Found"
        else:
            file_flag = "Missing"

    return name or "", pkg, ver or "", file_flag, prod_id

def _run_audit(scope):
    """Exécute l’audit pour Chrome, retourne (result_data, found_any)."""
    result_data = {}
    found_any = False

    for term in _normalize_tools(tools):
        section = term.capitalize()
        name, pkg, ver, file_flag, prod_id = _detect_one(term)

        result_data[section] = {
            "Name": name,
            "Package": pkg,
            "Version": ver,
            "File": file_flag,
            "Id": prod_id
        }
        if name or pkg or ver:
            found_any = True

    WAPT.write_audit_data_if_changed(scope, "audit_data", result_data)
    WAPT.write_audit_data_if_changed(scope, "result", "OK" if found_any else "NotFound")
    return result_data, found_any

def _zap_chrome(result_data):
    """Exécute msizap.exe T {GUID} pour Chrome trouvé."""
    zap_exe = makepath(persistent_dir, "msi", "msizap.exe")
    if not os.path.exists(zap_exe):
        print(f"[ERROR] msizap.exe introuvable : {zap_exe}")
        return

    for app, info in result_data.items():
        prod_id = info.get("Id") or ""
        if prod_id and prod_id.startswith("{"):
            cmd = f'"{zap_exe}" T {prod_id}'
            print(f"[ZAP] {cmd}")
            run(cmd)

def _cleanup_persistent_msis():
    """Supprime persistent_dir\\msi après utilisation."""
    msi_dir = makepath(persistent_dir, "msi")
    if os.path.isdir(msi_dir):
        print(f"[CLEANUP] Suppression du répertoire: {msi_dir}")
        shutil.rmtree(msi_dir, ignore_errors=True)

# ----------------- Audit principal -----------------
def audit():
    try:
        # 1) Audit avant
        before_data, found_any = _run_audit(AUDIT_SCOPE_BEFORE)

        # 2) Si Chrome trouvé, zapper avec msizap
        if found_any:
            _zap_chrome(before_data)

        # 3) Audit après
        _run_audit(AUDIT_SCOPE_AFTER)
        return "OK" if found_any else "NotFound"

    except Exception as e:
        print("[ERROR] Exception durant l'audit :", e)
        print(traceback.format_exc())
        return "ERROR"

    finally:
        # 4) Nettoyage final
        _cleanup_persistent_msis()

Here you go, if that's of any use. 🙃
Answer