A flexible PowerShell toolkit for deploying, uninstalling, and automatically maintaining software via the Windows Package Manager (Winget) in Microsoft Intune.
.
├── Detection-Script/
│ └── Detect-WingetApp.ps1
│
├── Install-Uninstall-Script/
│ ├── intune-package/
│ │ └── Manage-WingetApp.intunewin
│ └── source/
│ └── Manage-WingetApp.ps1
│
└── Maintenance-Script/
├── all/
│ ├── Detection-WingetUpdates.ps1
│ └── Remediation-WingetUpdates.ps1
├── auto/
│ ├── Detection-WingetUpdates.ps1
│ └── Remediation-WingetUpdates.ps1
└── specified/
├── Detection-WingetUpdates.ps1
└── Remediation-WingetUpdates.ps1
To ensure transparency during deployment and enable tracking for automated updates, the framework stores logs and inventory state in a central location on the client endpoint (C:\ProgramData\IntuneWingetManagement\).
- Log Directory:
C:\ProgramData\IntuneWingetManagement\Logs - Inventory Database:
C:\ProgramData\IntuneWingetManagement\WingetInventory.json
-
Centralized Logging All installation, uninstallation, detection, and remediation events are logged here with timestamps. This is essential for troubleshooting issues directly on client endpoints.
-
JSON Inventory Database (
WingetInventory.json)- Automatically generated/updated when apps are installed/uninstalled via
Manage-WingetApp.ps1. - Stores metadata (such as
AppID, installation date, andInstallerType) for every application deployed through this framework. - Serves as the single source of truth for the
auto/maintenance mode, ensuring that auto-updates only apply to managed applications without interfering with software installed manually by users.
- Automatically generated/updated when apps are installed/uninstalled via
When configuring Intune Win32 Apps or Proactive Remediations, always set "Run script as 32-bit process on 64-bit clients" to No.
Running under 32-bit PowerShell causes path redirection errors for %ProgramFiles%, registry lookup failures (missing standard HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall keys), and context mismatches when interacting with winget.exe in the SYSTEM context.
Detect-WingetApp.ps1
- Purpose: Custom Intune detection script used to verify whether a specific application is already installed on the endpoint.
Before using this in Intune, the $AppId variable must be adjusted directly inside the Detect-WingetApp.ps1 file for the respective application (e.g., $AppID = "Notepad++.Notepad++" or $AppID = "VideoLAN.VLC").
Manage-WingetApp.ps1
- Purpose: Universal Win32 App deployment script that handles both the installation and uninstallation of specified Winget packages.
| Parameter | Required | Type | Allowed Values | Description |
|---|---|---|---|---|
-Action |
Yes | String | Install, Uninstall |
Defines whether to install or remove the specified application. |
-AppId |
Yes | String | String | The exact Winget package ID (e.g., Notepad++.Notepad++). |
-InstallerType |
No | String | wix, nullsoft, msi, exe, inno, burn, msix, portable, zip |
Overrides/forces a specific installer architecture during installation. |
-CustomUninstallString |
No | String | String | Custom uninstallation command line used as a fallback if Winget uninstallation fails or isn't supported. |
- Example (Notepad++, VLC):
-
Install without InstallerType
powershell.exe -ExecutionPolicy Bypass -File ".\Manage-WingetApp.ps1" -Action Install -AppId "Notepad++.Notepad++" -
Install with InstallerType
powershell.exe -ExecutionPolicy Bypass -File ".\Manage-WingetApp.ps1" -Action Install -AppId "VideoLAN.VLC" -InstallerType "nullsoft"In some cases it is better to use a custom InstallerType:
- Bypass Default Installer Conflicts: Winget packages sometimes default to installer formats (like
.msior generic.exe) that may fail in the IntuneSYSTEMcontext or trigger unwanted reboot prompts. Specifying an explicit type (e.g.,nullsoftfor NSIS-based installers like VLC) forces Winget to download and parse the exact installer framework required for clean, silent deployments. - Ensure Silent Execution: Certain Winget manifests do not pass the correct silent flags by default for all available installer types. Forcing a specific installer type (e.g.,
inno,wix, ormsi) guarantees that Winget applies the matching standardized quiet switches (/VERYSILENT,/qn, etc.). - Target Specific Application Architectures: When an application provides multiple installer architectures under a single Winget ID, overriding the installer type helps ensure the script deploys the intended binary build reliably across all managed endpoints.
- Bypass Default Installer Conflicts: Winget packages sometimes default to installer formats (like
-
Uninstall:
powershell.exe -ExecutionPolicy Bypass -File ".\Manage-WingetApp.ps1" -Action Uninstall -AppId "Notepad++.Notepad++"- Sometimes it is better to use the original application uninstaller method, like this (especially VideoLAN.VLC). The script also attempts to locate an uninstallation method locally (in case Winget or a custom command fails).
powershell.exe -ExecutionPolicy Bypass -File .\Manage-WingetApp.ps1 -Action unInstall -AppId "VideoLAN.VLC" -CustomUninstallString '"%ProgramFiles%\VideoLAN\VLC\uninstall.exe" /S'
- Sometimes it is better to use the original application uninstaller method, like this (especially VideoLAN.VLC). The script also attempts to locate an uninstallation method locally (in case Winget or a custom command fails).
-
Manage-WingetApp.intunewin
- Purpose: Pre-packaged .intunewin file ready for direct upload to the Microsoft Intune Admin Center (win32-APP).
The Maintenance-Script/ folder contains script pairs designed for Microsoft Intune Proactive Remediations. These scripts automate software patch management via WinGet across your endpoints.
The framework provides three distinct deployment modes depending on your update strategy:
└── Maintenance-Script/
├── all/
│ ├── Detection-WingetUpdates.ps1
│ └── Remediation-WingetUpdates.ps1
├── auto/
│ ├── Detection-WingetUpdates.ps1
│ └── Remediation-WingetUpdates.ps1
└── specified/
├── Detection-WingetUpdates.ps1
└── Remediation-WingetUpdates.ps1
all/Full System Upgrade- Scope: Upgrades all WinGet-supported applications installed on the endpoint.
- Behavior: Runs
winget upgrade --allto keep all software updated regardless of how it was originally installed.
auto/Managed Applications Only (recommended)- Scope: Upgrades only applications that were deployed via this framework.
- Behavior: Checks against a local JSON tracking database created during app deployment. Unmanaged or user-installed software is ignored.
specified/Targeted Application List- Scope: Upgrades a curated list of applications.
- Behavior: Targets only specific AppID entries hardcoded inside an array within the script (e.g.,
$appsToUpdate= @("Notepad++.Notepad++", "VideoLAN.VLC")).
The detection script features an adaptable schedule matrix ($schedule) that allows IT admins to strictly enforce maintenance windows according to their organizational needs:
- Tailored Schedule: Fully configurable day-by-day and time-by-time array. Set custom execution slots, block entire days (
@()), or allow full 24-hour windows (00:00 - 23:59). - Multi-Window Support: Define multiple active periods within a single day to align with lunch breaks, off-peak hours, or specific shift schedules.
- Resource Optimization: If a check occurs outside an active window, the script exits cleanly without triggering remediation—preventing unwanted background scans and CPU usage during peak working hours.
To balance patch compliance with user productivity, notification titles, messages, and delay timers can be tailored per environment:
- Pre-Update Toast Warning: Customize the header (
$UpdateMessageTitle) and message body ($UpdateMessageText) sent to the lower-right Windows Notification Center to inform users before patching starts. - Flexible Countdown Timer: Adjust the wait duration (
$UpdateToWaitAfterMessage) in seconds—giving end users adequate time to save their work or close sensitive applications. - Completion Confirmation: Set a final notification (
$UpdateFinishedTitle/$UpdateFinishedText) to signal when background installation has concluded.
- Author: manuel-stgr
- License: Distributed under the MIT License. See LICENSE for details.