tis-meshagent-template icon

Mesh Agent Template

Paquet d’installation silencieuse pour Mesh Agent Template

0.2.1.3-84
System and network
System and network

tis-meshagent

Prerequis

⚠️ Ce paquet nécessite la mise en place préalable d’un serveur MeshCentral.
Avant toute installation, assurez-vous que votre serveur MeshCentral est correctement configuré et fonctionnel.

Vous pouvez suivre l’une des documentations officielles selon votre environnement d’installation :


Configuration

Récupération de l’agent

⚠️ Assurez-vous d’être connecté à l’interface web MeshCentral avant de procéder.

Pour télécharger les binaires de l’agent, rendez-vous sur l’URL de votre serveur, par exemple :
https://mesh.fschelfaut.lan/meshagents

Récupérez les agents Windows pour x86 et x64.


Modification du paquet

Une fois les binaires récupérés, exécutez la fonction update-package.

  1. Renommez le paquet comme souhaitez.
  2. Modifiez la configuration en éditant les champs nécessaires dans la colonne Value (touche F2).
    Les paramètres à obligatoirement modifier sont : mesh_server, mesh_port et mesh_deviceGroup
  3. Sélectionnez le binaire meshagent en version x86 (s’il n’est pas déjà présent à la racine du paquet).
  4. Sélectionnez le binaire meshagent en version x64 (s’il n’est pas déjà présent à la racine du paquet).

La configuration du paquet est terminée !
Vous pouvez build et upload le paquet sur votre dépôt WAPT.

Les paquets PREPROD sont des paquets construits via LUTI. Ils restent généralement 5 jours en PREPROD, après quoi un deuxième scan VirusTotal est effectué pour vérifier que le status n'a pas changé.
Si le paquet réussit ce dernier contrôle, il est promu en PROD et publié sur le store.

  • package: tis-meshagent-template
  • name: Mesh Agent Template
  • version: 0.2.1.3-84
  • categories: System and network
  • maintainer: WAPT Team,Tranquil IT,Jimmy PELÉ,Hubert TOUVET
  • licence: Apache 2.0
  • locale: all
  • target_os: windows
  • impacted_process: MeshAgent,meshagent
  • architecture: all
  • signature_date:
  • size: 14.97 Ko
  • installed_size: 4.16 Mo
  • conflicts :

package           : tis-meshagent-template
version           : 0.2.1.3-84
architecture      : all
section           : base
priority          : optional
name              : Mesh Agent Template
categories        : System and network
maintainer        : WAPT Team,Tranquil IT,Jimmy PELÉ,Hubert TOUVET
description       : Run update-package-sources then build-upload and deploy
depends           : 
conflicts         : tis-mesh_agent_service
maturity          : PREPROD
locale            : all
target_os         : windows
min_wapt_version  : 2.1
sources           : 
installed_size    : 4158016
impacted_process  : MeshAgent,meshagent
description_fr    : 
description_pl    : 
description_de    : 
description_es    : 
description_pt    : 
description_it    : 
description_nl    : 
description_ru    : 
audit_schedule    : 
editor            : 
keywords          : 
licence           : Apache 2.0
homepage          : 
package_uuid      : c89cc8a5-21e5-4723-a800-2fcfd6dcd542
valid_from        : 
valid_until       : 
forced_install_on : 
changelog         : 
min_os_version    : 
max_os_version    : 
icon_sha256sum    : 75919930641ca94d1e743ff8f41ac4f2f853ca4a4582c9bac1e8aa749fd735c1
signer            : test
signer_fingerprint: b82fc8ef4a4475c0f69ac168176c2bfc58f572eb716c4eadd65e4785c155dd8e
signature_date    : 2026-03-11T13:21:53.000000
signed_attributes : package,version,architecture,section,priority,name,categories,maintainer,description,depends,conflicts,maturity,locale,target_os,min_wapt_version,sources,installed_size,impacted_process,description_fr,description_pl,description_de,description_es,description_pt,description_it,description_nl,description_ru,audit_schedule,editor,keywords,licence,homepage,package_uuid,valid_from,valid_until,forced_install_on,changelog,min_os_version,max_os_version,icon_sha256sum,signer,signer_fingerprint,signature_date,signed_attributes
signature         : iQnO/c5cF+mYhzjaL6w0ySPF8NrgLXYre53tq7MV/m/PKWsKF7VbDxixYQ6i3Yku74t7kMvtLOwPmqMrvHNTwPEt3eSzXJUQPuj0fvhcgmBdc91XE7zZZjWi6LnxnhjGRx6bv6XVSGQeFu/BnBKtHYgG5d0Ww85g3DBlMdudOvys/kxJHYdMptGdcn/atzcEhQtcgsmWPfEVlWPmFPKwudrF2y02mun2p8DMbEoQykBg9ZzCrZQmUV/l147d1Hs9xJ0It/lxitHBePU29Q9WwPK4HS1Td7B69+vqRxHn929qo7GNZCY80l4zbDH7fw/cfX2E/nDAO3eMyJAD14odYQ==

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

config_file = "mesh-conf.json"


def install():
    # Declaring local variables
    package_version = control.get_software_version()
    app_name = control.name
    mesh_config_dict = json_load_file(get_persistent_package_file(config_file))
    if mesh_config_dict["mesh_companyName"] != mesh_config_dict["mesh_serviceName"]:
        app_dir = makepath(programfiles, mesh_config_dict["mesh_companyName"], mesh_config_dict["mesh_serviceName"])
    else:
        app_dir = makepath(programfiles, mesh_config_dict["mesh_companyName"])
    app_path = makepath(app_dir, mesh_config_dict["mesh_fileName"] + ".exe")
    app_uninstallkey = mesh_config_dict["mesh_serviceName"]

    if iswin64():
        bin_name = glob.glob("*64-*.exe")[0]
    else:
        bin_name = glob.glob("*32-*.exe")[0]

    if not running_as_system():
        print(
            f'WARNING: {app_name} will NOT be installed as SYSTEM account, that will cause issues since it is installed as user: {get_current_user()} ("Waiting for key: {mesh_config_dict["mesh_serviceName"]}" will not complete), please uninstall it manually before deployment'
        )

    def get_app_version(key):
        return get_version_from_binary(app_path, "FileVersion")

    # Installing the software
    install_exe_if_needed(
        bin_name,
        silentflags="-fullinstall",
        key=app_uninstallkey,
        min_version=package_version,
        get_version=get_app_version,
        accept_returncodes=[0, 1, 3010, 3221226356],
    )

    # Adding QuietUninstallString in registry for software
    quiet_uninstall_string = f'"{app_path}" {"-fulluninstall"}'
    wait_uninstallkey_present(app_uninstallkey)
    register_uninstall(app_uninstallkey, quiet_uninstall_string=quiet_uninstall_string)


def uninstall():
    # Declaring local variables
    mesh_config_dict = json_load_file(get_persistent_package_file(config_file))
    app_dir = makepath(programfiles, mesh_config_dict["mesh_companyName"])
    app_uninstallkey = mesh_config_dict["mesh_serviceName"]

    # Removing remaining folders if possible
    if isdir(app_dir):
        killalltasks(control.get_impacted_process_list())
        wait_uninstallkey_absent(app_uninstallkey)
        print("Removing: %s" % (app_dir))
        remove_tree(app_dir)


def audit():
    # Declaring local variables
    mesh_config_dict = json_load_file(get_persistent_package_file(config_file))
    if mesh_config_dict["mesh_companyName"] != mesh_config_dict["mesh_serviceName"]:
        app_dir = makepath(programfiles, mesh_config_dict["mesh_companyName"], mesh_config_dict["mesh_serviceName"])
    else:
        app_dir = makepath(programfiles, mesh_config_dict["mesh_companyName"])
    app_path = makepath(app_dir, mesh_config_dict["mesh_fileName"] + ".exe")
    app_name = mesh_config_dict["mesh_displayName"]
    mesh_regkey = r"SOFTWARE\Open Source\%s" % mesh_config_dict["mesh_serviceName"]
    meshserverurl = registry_readstring(HKEY_LOCAL_MACHINE, mesh_regkey, "MeshServerUrl")
    nodeid = registry_readstring(HKEY_LOCAL_MACHINE, mesh_regkey, "NodeId")
    remotedesktopurl = "https://%s:%s/?viewmode=11&gotonode=%s&hide=25" % (mesh_config_dict["mesh_server"], mesh_config_dict["mesh_port"], nodeid)

    print(remotedesktopurl)
    """
    // SERVICE_STOPPED				  1    The service is not running.
    // SERVICE_START_PENDING		  2    The service is starting.
    // SERVICE_STOP_PENDING			  3    The service is stopping.
    // SERVICE_RUNNING				  4    The service is running.
    // SERVICE_CONTINUE_PENDING		  5    The service continue is pending.
    // SERVICE_PAUSE_PENDING		  6    The service pause is pending.
    // SERVICE_PAUSED				  7    The service is paused.
    // SERVICE_NOT_INSTALLED		100    The service is not installed.
    """
    # nodeid = run([app_path, "-nodeid"]).splitlines()[0]
    # run(f'"{app_path}" -exec "console.log(_MSH().meshServiceName);process.exit();"')
    state = run(f'"{app_path}" {"state"}')  # , accept_returncodes=[0, 1, 2, 3, 4, 5, 6, 7, 100]
    state_str = state.strip()
    print("%s service state is:" % app_name)
    print(state_str)
    if state_str.lower() == "RUNNING".lower():
        result = "OK"
    elif state_str.lower() == "NOT INSTALLED".lower():
        result = "WARNING"
        print(
            'INFO: "Not installed" Service state may be incorrect status, you may need to update your Mesh Server and Mesh Agent (more info here: https://github.com/Ylianst/MeshAgent/issues/87)'
        )
    else:
        result = "WARNING"

    try:
        WAPT.write_audit_data_if_changed("mesh", "meshserverurl", meshserverurl, keep_days=365)
        WAPT.write_audit_data_if_changed("mesh", "nodeid", nodeid, keep_days=365)
        WAPT.write_audit_data_if_changed("mesh", "agenthash", registry_readstring(HKEY_LOCAL_MACHINE, mesh_regkey, "AgentHash"), keep_days=365)
        WAPT.write_audit_data_if_changed("mesh", "remotedesktopurl", remotedesktopurl, keep_days=365)
    except:
        print("ERROR: write_audit_data failed")
        result = "ERROR"

    return result


def get_persistent_package_file(fname):
    if isdir(makepath(os.getcwd(), "WAPT", "persistent")):
        return makepath(os.getcwd(), "WAPT", "persistent", fname)
    else:
        return makepath(WAPT.persistent_root_dir, control.package_uuid, fname)


def get_persistent_package_dir():
    if isdir(makepath(os.getcwd(), "WAPT", "persistent")):
        return makepath(os.getcwd(), "WAPT", "persistent")
    else:
        return makepath(WAPT.persistent_root_dir, control.package_uuid)

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


def get_persistent_package_file(fname):
    if isdir(makepath(os.getcwd(), "WAPT", "persistent")):
        return makepath(os.getcwd(), "WAPT", "persistent", fname)
    else:
        return makepath(WAPT.persistent_root_dir, control.package_uuid, fname)


config_file = "mesh-conf.json"
mesh_config_dict_old = json_load_file(get_persistent_package_file(config_file))


def update_package():
    # Declaring local variables
    package_updated = False

    # Gathering informations for the package
    if "template" in control.package:
        package = waptguihelper.input_dialog(
            control.package, "Please change the template suffix with a term corresponding to your Mesh Agent", control.package
        )
        control.package = package
        control.save_control_to_wapt()

    dialog_response = waptguihelper.grid_dialog(
        "Adapt your Agent parameters (corresponding to: meshcentral-data/config.json)",
        format_dict_to_grid(mesh_config_dict_old),
        0,
        '{"columns":[{"propertyname":"parameter","datatype":"String","required":false,"readonly":true,"width":153},{"propertyname":"value","datatype":"String","required":false,"readonly":false,"width":172}]}',
    )
    json_write_file(get_persistent_package_file(config_file), format_grid_to_dict(dialog_response))
    mesh_config_dict = json_load_file(get_persistent_package_file(config_file))

    # Getting binaries
    if glob.glob("*.exe"):
        ask_for_binaries = waptguihelper.message_dialog(control.package, "Do you want to deploy new Mesh Agent binaries ?", waptguihelper.MB_YESNO)
    else:
        ask_for_binaries = 6

    # Deleting binaries
    if ask_for_binaries == 6:
        for f in glob.glob("*.exe") + glob.glob("*.msi"):
            print("Removing: %s" % f)
            remove_file(f)

        bin_path_x86 = waptguihelper.filename_dialog(
            "Please provide x86 Mesh Agent",
            "",
            "%s32-%s.exe" % (mesh_config_dict["mesh_fileName"].replace(" ", ""), mesh_config_dict["mesh_deviceGroup"].replace(" ", "")),
            "EXE Files|*.exe",
        )
        bin_path_x64 = waptguihelper.filename_dialog(
            "Please provide x64 Mesh Agent",
            "",
            "%s64-%s.exe" % (mesh_config_dict["mesh_fileName"].replace(" ", ""), mesh_config_dict["mesh_deviceGroup"].replace(" ", "")),
            "EXE Files|*.exe",
        )
        bin_name_x64 = bin_path_x64.split(os.sep)[-1].split(".")[0]
        if bin_path_x86:
            bin_name_x86 = bin_path_x86.split(os.sep)[-1].split(".")[0]
            filecopyto(bin_path_x86, basedir)
            if not bin_name_x86 in control.impacted_process:
                control.impacted_process = control.impacted_process + "," + bin_name_x86
                control.save_control_to_wapt()
        filecopyto(bin_path_x64, basedir)
        if not bin_name_x64 in control.impacted_process:
            control.impacted_process = control.impacted_process + "," + bin_name_x64
            control.save_control_to_wapt()

        # Changing version of the package
        version = get_file_properties(bin_path_x64).get("FileVersion", control.get_software_version())
        if Version(version) > Version(control.get_software_version()):
            print("Software version updated (from: %s to: %s)" % (control.get_software_version(), Version(version)))
            package_updated = True
        else:
            print("Software version up-to-date (%s)" % Version(version))
        control.set_software_version(version)
        control.save_control_to_wapt()
    if not mesh_config_dict["mesh_fileName"] in control.impacted_process:
        control.impacted_process = control.impacted_process + "," + mesh_config_dict["mesh_fileName"]
        control.save_control_to_wapt()

    # Editing control
    if not "template" in control.package:
        control.name = mesh_config_dict["mesh_displayName"]
        control.description = mesh_config_dict["mesh_displayName"]
        control.save_control_to_wapt()

    # Validating or not update-package-sources
    return package_updated


def format_dict_to_grid(dict_data, key_name="parameter", value_name="value"):
    dict_list_data = []
    for section in dict_data:
        dict_list_data.append({key_name: section, value_name: dict_data[section]})
    return json.dumps(dict_list_data)


def format_grid_to_dict(dict_list_data, key_name="parameter", value_name="value"):
    dict_data = {}
    for entry in dict_list_data:
        dict_data.update({entry[key_name]: entry[value_name]})
    return dict_data

79adb053d90376e922e2381fa70f36ceabdef4533538033201ed5029fce9d3e5 : WAPT/README.md
5db3352b0500766e5200fccafe8d5482db2359fbd420581b33add05e6950554e : WAPT/README_fr.md
01ca7fe94636e5a08fcb73849d3b5df25d51e2c82f4dd1a08f01798b25899819 : WAPT/certificate.crt
c93319868827d9ec45281e366ae3118b62d3138ddb93892f2088e7976364b36f : WAPT/changelog.txt
538bc5ec40996d8c29b1cd05b48d082506cf152c4a08e46d8e72efaf288f483c : WAPT/control
75919930641ca94d1e743ff8f41ac4f2f853ca4a4582c9bac1e8aa749fd735c1 : WAPT/icon.png
df263c72463ec99b91797ea95d4086c5f618f6b18d1d82eaa851eb186491810d : WAPT/persistent/mesh-conf.json
360d3e764911599cc549020bbbf394de3b17a3aeae962a74f18654748ec254c5 : luti.json
4cd62dada82b6bcb161275f1b94f6da8e1655ffc2ec675e8be9e2f41def234fa : setup.py
a1e856f29393cb2906d5431d3dc5da639291a8885412907ab9c1a46ff122ae7d : update_package.py

0.2.1.3-81

Package can now be fully customized interactively with update_package
Configuration is now based on JSON file "mesh-conf.json" graphically edited in update_package
Now using persistent folder to call JSON file locally with get_persistent_package_file()
Autofilling impacted_process
Fix uninstall by adding QuietUninstallString 
Reverse waptguihelper.grid_dialog for easy editing
Now asking for port instead of mixing it with server