Table of Contents
Create a PowerShell script for deployment
This PowerShell script simplifies the daily update process for 7-Zip by checking the Winget GitHub repository for the latest version, downloading it if it’s newer than the current installation (or if not installed), and setting up a scheduled task to perform this check daily at 12 AM.
$scriptBlock = {
# GitHub API URL for the app manifest.
$apiUrl = "https://api.github.com/repos/microsoft/winget-pkgs/contents/manifests/7/7zip/7zip"
# Fetch version folders then filter only version folders.
$versions = Invoke-RestMethod -Uri $apiUrl -Headers @{ 'User-Agent' = 'PowerShell' }
$versionFolders = $versions | Where-Object { $_.type -eq "dir" }
# Extract and sort version numbers to get the latest version.
$sortedVersions = $versionFolders | ForEach-Object { $_.name } | Sort-Object {[version]$_} -Descending -ErrorAction SilentlyContinue
$latestVersion = $sortedVersions[0]
# Get contents of the latest version folder to find the .installer.yaml file.
$latestApiUrl = "$apiUrl/$latestVersion"
$latestFiles = Invoke-RestMethod -Uri $latestApiUrl -Headers @{ 'User-Agent' = 'PowerShell' }
$installerFile = $latestFiles | Where-Object { $_.name -like "*.installer.yaml" }
# Download and parse YAML content to get the Url of the latest installer file.
$yamlUrl = $installerFile.download_url
$yamlContent = Invoke-RestMethod -Uri $yamlUrl -Headers @{ 'User-Agent' = 'PowerShell' }
$yamlString = $yamlContent -join "`n"
$installerUrls = [regex]::Matches($yamlString, "InstallerUrl:\s+(http[^\s]+)") | ForEach-Object { $_.Groups[1].Value }
$installerUrl = $installerUrls[1]
# Check the installed version number of the app and store it to the $installedVersion variable.
$regPaths = @(
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
"HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
)
foreach ($regPath in $regPaths) {
$apps = Get-ChildItem $regPath -ErrorAction SilentlyContinue
foreach ($app in $apps) {
$props = Get-ItemProperty $app.PSPath
if ($props.DisplayName -like "*7-zip*") {
$installedVersion = $($props.DisplayVersion)
}
}
}
# Download the latest installer then starting install or update the app if:
# - The installed version is older than the latest version.
# - The app is not installed ( $installedVersion = $null ).
if ($installedVersion -lt $latestVersion) {
$webClient = [System.Net.WebClient]::new()
$webClient.DownloadFile($installerUrl, "$env:TEMP\7zip-latest.exe")
# If the app is running, stop it before processing the update.
$process = Get-Process -ProcessName '7zip' -ErrorAction SilentlyContinue
if ($process) {
$process | Stop-Process -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 2
}
# Start the install or update process.
Start-Process -FilePath "$env:TEMP\7zip-latest.exe" -ArgumentList '/S' -Wait
# Cleanup.
Remove-Item -Path "$env:TEMP\7zip-latest.exe" -Force -ErrorAction SilentlyContinue
}
}
# Create the C:\IntuneScripts folder if it not exist.
if (-Not (Test-Path -Path 'C:\IntuneScripts')) {
New-Item -Path 'C:\IntuneScripts' -ItemType Directory
}
# Create a PowerShell from the $scriptBlock in the C:\IntuneScripts folder.
$scriptBlock.ToString() | Out-File -FilePath 'C:\IntuneScripts\7zip-update.ps1' -Encoding UTF8 -Force
# Create a schedule task. The task will execute the PowerShell script "C:\IntuneScripts\7zip-update.ps1" every day at 12AM.
$trigger = New-ScheduledTaskTrigger -Daily -At 12AM -RandomDelay (New-TimeSpan -Minutes 10)
$action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument '-NoProfile -ExecutionPolicy ByPass -WindowStyle Hidden -File "C:\IntuneScripts\7zip-update.ps1"'
$settings = New-ScheduledTaskSettingsSet -StartWhenAvailable -RestartCount 3
$principal = New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount -RunLevel Highest
$splat = @{
TaskName = '7zip Update'
Trigger = $trigger
Action = $action
Settings = $settings
Principal = $principal
TaskPath = '\IntuneTasks\'
}
Register-ScheduledTask @splat
Below is a detailed explanation of what each part of the PowerShell script does. The script is designed to automatically update 7-Zip on Windows managed devices using Microsoft Intune.
1. Define the script block
$scriptBlock = {
...
}
- Everything within this block is the actual update logic for the app.
- This will later be saved as a .ps1 script file and scheduled to run daily.
2. Set the GitHub API URL
$apiUrl = "https://api.github.com/repos/microsoft/winget-pkgs/contents/manifests/7/7zip/7zip"
- Points to the 7-Zip manifest folder in the winget-pkgs GitHub repository.
- Used to dynamically retrieve the latest available version info.
3. Fetch and sort available versions
$versions = Invoke-RestMethod -Uri $apiUrl -Headers @{ 'User-Agent' = 'PowerShell' }
$versionFolders = $versions | Where-Object { $_.type -eq "dir" }
$sortedVersions = $versionFolders | ForEach-Object { $_.name } | Sort-Object {[version]$_} -Descending
$latestVersion = $sortedVersions[0]
- Fetches all folders (each represents a version) under the 7-Zip manifest path.
- Filters folders, extracts version names, and sorts them in descending order.
- Grabs the latest version.
PS C:\> $sortedVersions
25.00
24.09
24.08
24.07
24.06
24.05
23.01
22.01
22.00
21.07
21.06
19.00.00.0
16.04
Alpha
...
4. Get the content of the YAML installer file
$latestApiUrl = "$apiUrl/$latestVersion"
$latestFiles = Invoke-RestMethod -Uri $latestApiUrl -Headers @{ 'User-Agent' = 'PowerShell' }
$installerFile = $latestFiles | Where-Object { $_.name -like "*.installer.yaml" }
- Navigates to the latest version folder.
- Searches for a .installer.yaml file, which contains metadata including the download URL for the installer.
PS C:\> $installerFile
name : 7zip.7zip.installer.yaml
path : manifests/7/7zip/7zip/25.00/7zip.7zip.installer.yaml
sha : 5e4a168141b6de6a40aad780b6bead2b05afb230
size : 3009
url : https://api.github.com/repos/microsoft/winget-pkgs/contents/manifests/7/7zip/7zip/25.00/7zip.7zip.installer.yaml?ref=master
html_url : https://github.com/microsoft/winget-pkgs/blob/master/manifests/7/7zip/7zip/25.00/7zip.7zip.installer.yaml
git_url : https://api.github.com/repos/microsoft/winget-pkgs/git/blobs/5e4a168141b6de6a40aad780b6bead2b05afb230
download_url : https://raw.githubusercontent.com/microsoft/winget-pkgs/master/manifests/7/7zip/7zip/25.00/7zip.7zip.installer.yaml
type : file
_links : @{self=https://api.github.com/repos/microsoft/winget-pkgs/contents/manifests/7/7zip/7zip/25.00/7zip.7zip.installer.yaml?ref=master; git=https://api.github.com/repos/microsoft/winget-pkgs/git/blobs/5e4a168141b6de6a40aad780b6bead2b05afb230; html=https://github.com/microsoft/winget-pkgs/blob/master/manifests/7/7zip/7zip/25.00/7zip.7zip.installer.yaml}
5. Extract the Installer URL (the direct download link)
$yamlUrl = $installerFile.download_url
$yamlContent = Invoke-RestMethod -Uri $yamlUrl -Headers @{ 'User-Agent' = 'PowerShell' }
$null = ($yamlContent -join "`n") -match "InstallerUrl:\s+(http.*)"
$installerUrl = $Matches[1]
- Downloads the YAML content using the Invoke-RestMethod cmdlet.
- Uses a regular expression to extract the InstallerUrl value (the actual .exe download link).
- Saves the installer URL for use later in a variable.
PS C:\> $installerUrl
https://7-zip.org/a/7z2500-x64.exe
6. Check the installed version (Registry Scan)
$regPaths = @(
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
"HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
)
foreach ($regPath in $regPaths) {
$apps = Get-ChildItem $regPath -ErrorAction SilentlyContinue
foreach ($app in $apps) {
$props = Get-ItemProperty $app.PSPath
if ($props.DisplayName -like "*7-zip*") {
$installedVersion = $($props.DisplayVersion)
}
}
}
- Scans both 32-bit and 64-bit uninstall registry paths.
- Looks for any installed app with DisplayName containing “7-Zip”.
- Retrieves the DisplayVersion property to compare with the latest ( $latestVersion ).
7. Install or update the application
if ($installedVersion -lt $latestVersion) {
$webClient = [System.Net.WebClient]::new()
$webClient.DownloadFile($installerUrl, "$env:TEMP\7zip-latest.exe")
# If the app is running, stop it before processing the update.
$process = Get-Process -ProcessName '7zip' -ErrorAction SilentlyContinue
if ($process) {
$process | Stop-Process -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 2
}
# Start the install or update process.
Start-Process -FilePath "$env:TEMP\7zip-latest.exe" -ArgumentList '/S' -Wait
# Cleanup.
Remove-Item -Path "$env:TEMP\7zip-latest.exe" -Force -ErrorAction SilentlyContinue
}
- If 7-Zip is not installed, or if the installed version is older than the latest available:
- Downloads the latest installer to the temp directory.
- Stops any running instance of 7-Zip.
- Runs the installer in silent mode.
- Deletes the installer afterward.
With the script block completed, the next steps in Intune include creating a folder, writing the PowerShell update script, and setting up a scheduled task to run the script daily.
8. Create the C:\IntuneScripts directory
if (-Not (Test-Path -Path 'C:\IntuneScripts')) {
New-Item -Path 'C:\IntuneScripts' -ItemType Directory
}
- Ensures the folder C:\IntuneScripts exists to store the script file.
Directory: C:\
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 7/15/2025 5:11 PM IntuneScripts
9. Save the script to a .ps1 file
$scriptBlock.ToString() | Out-File -FilePath 'C:\IntuneScripts\7zip-update.ps1' -Encoding UTF8 -Force
- Converts the script block into a string and writes it to a .ps1 file (C:\IntuneScripts\7zip-update.ps1).
- This is the file that the scheduled task will execute.
PS C:\> Get-ChildItem -Path 'C:\IntuneScripts'
Directory: C:\IntuneScripts
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 7/15/2025 5:16 PM 2812 7zip-update.ps1
10. Create a scheduled task
$trigger = New-ScheduledTaskTrigger -Daily -At 12AM -RandomDelay (New-TimeSpan -Minutes 10)
$action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument '-NoProfile -ExecutionPolicy ByPass -WindowStyle Hidden -File "C:\IntuneScripts\7zip-update.ps1"'
$settings = New-ScheduledTaskSettingsSet -StartWhenAvailable -RestartCount 3
$principal = New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount -RunLevel Highest
- Trigger: Runs every day at 12 AM with up to a 10-minute random delay (to reduce network congestion if deployed at scale).
- Action: Executes the saved PowerShell script in hidden, bypass mode.
- Settings: Automatically restarts if the system was off at the scheduled time.
- Principal: Runs under the SYSTEM account with highest privileges (no user login required).
11. Register the scheduled task
$splat = @{
TaskName = '7Zip Update'
Trigger = $trigger
Action = $action
Settings = $settings
Principal = $principal
TaskPath = '\IntuneTasks\'
}
Register-ScheduledTask @splat
- Registers the scheduled task using all previously defined properties (trigger, action, principal, etc.).
- Task name: 7Zip Update, under the custom path \IntuneTasks\.
PS C:\> Register-ScheduledTask @splat
TaskPath TaskName State
-------- -------- -----
\IntuneTasks\ WinRAR Update Ready
Deploy the PowerShell script using Microsoft Intune
Once the script is created, there are several options to deploy it to managed devices using Microsoft Intune for updating the application. Check out the links below for more details.
Working with PowerShell scripts in Microsoft Intune
Before you begin
- When scripts are set to user context and the end user has administrator rights, by default, the PowerShell script runs under the administrator privilege.
- End users aren’t required to sign in to the device to execute PowerShell scripts.
- The Intune management extension checks after every reboot for any new scripts or changes. After you assign the policy to the Microsoft Entra groups, the PowerShell script runs, and the run results are reported.
- Once the script executes, it doesn’t execute again unless there’s a change in the script or policy. If the script fails, the Intune management extension retries the script three times for the next three consecutive Intune management extension check-ins.
- A PowerShell script assigned to the device will run for every new user that signs in, except on multi-session SKUs where user check-in is disabled.
- PowerShell scripts are executed before Win32 apps run. In other words, PowerShell scripts execute first. Then, Win32 apps execute.
- PowerShell scripts time out after 30 minutes.
- Scripts deployed to clients running the Intune management extension will fail to run if the device’s system clock is exceedingly out of date by months or years. Once the system clock is brought up to date, script will run as expected.
- Do not include any type of sensitive information in scripts (such as passwords)
- Do not include Personally Identifiable Information (PII) in scripts
- Do not use scripts to collect PII from devices
- Always follow privacy best practices