Tech Posts

WinGet on Windows Server 2025: Guide to PowerShell Automation & Setup

The arrival of Windows Server 2025 marks a pivotal shift in server administration, finally bringing WinGet into the fold as a native, first-party package manager. For administrators tired of manual installations and inconsistent server builds, this is the moment we’ve been waiting for. This guide is your definitive resource for moving beyond basic commands to master a complete, end-to-end workflow. We’ll dive deep into everything from initial verification and Group Policy lockdown to building robust offline repositories and creating intelligent, idempotent PowerShell scripts for fully automated software lifecycle management. The Definitive Guide to WinGet on Windows Server 2025 | GigXP.com

Server 2025 Deep Dive

The Definitive Administrator's Guide to WinGet and PowerShell on Windows Server 2025

From initial setup and offline deployment to advanced automation and logging, this is your end-to-end guide for mastering server package management.

1. Mastering the WinGet Environment

Windows Server 2025 ships with WinGet, but mastering it means understanding its interfaces and locking it down for enterprise use. We'll cover verification, the crucial choice between the CLI and the PowerShell module, and centralized control with Group Policy.

Initial Verification and Configuration

Unlike previous versions, Windows Server 2025 includes WinGet out of the box. However, the first step is always verification. Use these commands to confirm WinGet is functional and to get a snapshot of its configuration, which is invaluable for troubleshooting.


# Check the installed version of the WinGet client
winget --version

# Display detailed information, including log locations and configured sources
winget --info
                    

In some automated deployment scenarios, the `winget` command may not be immediately available. If so, you can manually trigger its registration with the following command in an elevated PowerShell prompt:


# Manually register the App Installer package family to make winget.exe available
Add-AppxPackage -RegisterByFamilyName -MainPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe
                    

CLI vs. PowerShell Module: A Strategic Choice

Choosing the right tool for the job is critical. The `winget.exe` CLI is great for quick, interactive tasks, but for any serious automation, the `Microsoft.WinGet.Client` PowerShell module is the only reliable option.

Feature winget.exe (CLI) Microsoft.WinGet.Client (PowerShell) Recommendation
Output Type Plain Text (Strings) PowerShell Objects Objects provide structured, reliable data for scripting.
Scripting Reliability Brittle Robust Object properties don't break when display formatting changes.
Error Handling Exit Codes, Error Strings Exception Objects, Status Properties Allows for precise, programmatic error handling.
Use Case Interactive Admin Tasks Automated Systems, CI/CD Use the right tool for the job.

Centralized Control with Group Policy

For enterprise environments, central management is non-negotiable. WinGet's Group Policy templates allow you to enforce security and configuration across your entire server fleet, turning it into a manageable, secure platform.

  • Enable App Installer Allowed Sources

    The most critical security policy. Use this to create a whitelist of approved internal repositories, effectively blocking access to all public sources and preventing unvetted software installations.

  • Disable Default & Microsoft Store Sources

    In server environments, access to public repositories is often undesirable. Explicitly disable the default `winget` community repository and the `msstore` source to tighten security.

  • Set Source Auto Update Interval

    Control how often clients refresh their package metadata cache. In large environments, increasing this interval from the default 5 minutes can significantly reduce network traffic to your internal repository.

2. Architecting Offline & Air-Gapped Deployments

Many server environments have restricted internet access. WinGet provides a powerful workflow for these scenarios, from a simple installer cache to a fully-featured private REST repository. The choice between them is a critical architectural decision for any offline environment.

Offline Deployment Models

Level 1: Local Package Cache

Use `winget download` on a staging machine to create a file share of installers. Simple to set up, but limited functionality on offline clients.

Level 2: Private REST Source

Host your own repository. This provides full `search`, `install`, and `upgrade` functionality for a truly managed offline experience.

Scripting Offline Installations

How you install software on the offline server depends on your chosen model. A private REST source allows for elegant, native WinGet commands, while a file cache requires more manual scripting.

Install-From-REST-Source.ps1


# First, ensure the private source is added (typically done once via GPO or script)
# winget source add --name "InternalRepo" -a "http://internal-repo.corp.local/api"

# List of application IDs to install from the internal repository
$packagesToInstall = @("7zip.7zip", "Internal.App.LegacyWrapper")

foreach ($packageId in $packagesToInstall) {
    Write-Host "Installing package: $packageId from InternalRepo..."
    # Use standard winget install, but specify the internal source
    Install-WinGetPackage -Id $packageId -Source "InternalRepo" -AcceptPackageAgreements -Scope Machine
}
                            

Install-From-File-Cache.ps1


$cachePath = "FileServer01WinGetCache"
$appsToInstall = @{ "Notepad++.Notepad++" = "Notepad++.Notepad++.8.6.5.exe" }

foreach ($app in $appsToInstall.GetEnumerator()) {
    $installerFile = Join-Path -Path $cachePath -ChildPath $app.Value
    if (Test-Path $installerFile) {
        # Silent switches vary by installer and must be known in advance
        $arguments = "/S" 
        Start-Process -FilePath $installerFile -ArgumentList $arguments -Wait
    }
}
                            

3. Automating the Software Lifecycle

This is where WinGet shines. We'll explore idempotent scripts for baseline server builds, create a robust and intelligent update solution, and schedule it all for unattended maintenance.

Intelligent Update Workflow

1. Find Packages

`Get-WinGetPackage`

2. Check for Updates

`Where-Object { $_.IsUpdateAvailable }`

3. Filter Exclusions

`Where-Object { $_.Id -notin $exclusionList }`

4. Apply Update

`Update-WinGetPackage`

Interactive PowerShell Scripts

Filter the scripts below to find the right automation for your task. Click a button to view the relevant code snippet.

Install-Baseline.ps1


# Import the PowerShell module for robust automation
Import-Module Microsoft.WinGet.Client -ErrorAction SilentlyContinue

# Path to the CSV file defining the software baseline
$baselineFile = "C:ScriptsServerBaseline.csv"

if (-not (Test-Path $baselineFile)) {
    Write-Error "Baseline definition file not found at $baselineFile"
    return
}

# Import the list of applications from the CSV
$appsToInstall = Import-Csv -Path $baselineFile

# Loop through each application defined in the baseline
foreach ($app in $appsToInstall) {
    # Idempotency Check: Verify if the package is already installed
    $installedPackage = Get-WinGetPackage -Id $app.PackageId -ErrorAction SilentlyContinue
    if ($installedPackage) {
        Write-Host "$($app.PackageId) is already installed. Skipping." -ForegroundColor Yellow
        continue
    }

    # Install the package
    Install-WinGetPackage -Id $app.PackageId -Source $app.Source -Scope Machine -AcceptPackageAgreements
}
                            

Perform-WinGet-Updates.ps1


# Import the PowerShell module
Import-Module Microsoft.WinGet.Client -ErrorAction SilentlyContinue

# Define a list of package IDs to exclude from automatic updates
$exclusionList = @(
    "Oracle.MySQL",
    "Microsoft.DotNet.SDK.6"
)

# Find all installed packages that have an update available and are not in the exclusion list
$updatablePackages = Get-WinGetPackage | Where-Object { 
    $_.IsUpdateAvailable -and $_.Id -notin $exclusionList 
}

if ($updatablePackages) {
    # Loop through the filtered list and apply updates
    foreach ($package in $updatablePackages) {
        $package | Update-WinGetPackage -AcceptPackageAgreements -Scope Machine
    }
}
                            

Create-Update-Task.ps1


# --- Task Configuration ---
$taskName = "Weekly WinGet Application Update"
$scriptPath = "C:ScriptsPerform-WinGet-Updates.ps1"
$principal = "NT AUTHORITYSYSTEM"

# --- Create the Task Action ---
$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-NoProfile -ExecutionPolicy Bypass -File `"$scriptPath`""

# --- Create the Task Trigger ---
$trigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Sunday -At 3am

# --- Create the Task Principal ---
$taskPrincipal = New-ScheduledTaskPrincipal -UserId $principal -LogonType ServiceAccount -RunLevel Highest

# --- Register the Scheduled Task ---
Register-ScheduledTask -TaskName $taskName `
    -Action $action `
    -Trigger $trigger `
    -Principal $taskPrincipal
                            

4. The Future: Declarative Setup with `winget configure`

Move beyond imperative scripts to a declarative model. Define the *desired state* of your server in a YAML file, and let WinGet and PowerShell DSC handle the rest. This is the essence of Infrastructure as Code (IaC).

Declarative Configuration Flow

1. Define State in YAML

`configuration.dsc.yaml`

2. Run `winget configure`

Single command execution

3. System is Compliant

Idempotent and repeatable

Example: `configuration.dsc.yaml`

This YAML file defines the desired state for a web server. It asserts the OS version, ensures specific WinGet packages are installed, and enables the IIS Windows Feature using PowerShell DSC. Applying it is as simple as running `winget configure -f C:PathToFile.yaml`.


# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2
properties:
  resources:
    - resource: Microsoft.WinGet.DSC/WinGetPackage
      directives:
        description: Install Microsoft PowerShell 7.
      settings:
        id: Microsoft.PowerShell
        source: winget
    - resource: Microsoft.WinGet.DSC/WinGetPackage
      directives:
        description: Install Visual Studio Code.
      settings:
        id: Microsoft.VisualStudioCode
        source: winget
    - resource: PSDscResources/WindowsFeature
      directives:
        description: Ensure the IIS Web Server role is installed.
      settings:
        Name: Web-Server
        Ensure: Present
configurationVersion: 0.2.0
                    

5. Advanced Logging & Troubleshooting

In an enterprise environment, robust logging and effective troubleshooting are essential for security, compliance, and operational stability. WinGet provides a comprehensive logging framework that enables a shift from reactive problem-solving to proactive, automated remediation.

Decoding Diagnostic Logs

WinGet creates a detailed log file for every operation, which is your primary source of truth for diagnosing failures. You can quickly open the log directory after any command by appending the `--logs` switch.


# Run an installation and immediately open the log directory to view the results.
winget install Microsoft.VisualStudioCode --logs --accept-package-agreements

# For deeper issues, enable verbose logging to see every detail.
winget install Microsoft.PowerShell --verbose-logs --accept-package-agreements
                    

Programmatic Error Handling

The true power of automation lies in handling errors at runtime. The PowerShell module returns objects with status and error codes, allowing your scripts to react intelligently to specific failure conditions, a capability impossible with simple text parsing.


$installResult = Install-WinGetPackage -Id "Some.App" -ErrorAction SilentlyContinue

if ($installResult.Status -ne 'Ok') {
    $errorCode = $installResult.InstallerErrorCode
    Write-Warning "Install failed with code: $errorCode"
    
    # React to specific, known error codes
    switch ($errorCode) {
        # Reboot required
        3010 { Write-Host "Flagging system for a required reboot." }
        # Another install is running
        1618 { 
            Write-Host "Another installation is in progress. Retrying in 5 minutes."
            Start-Sleep -Seconds 300
            # Add retry logic here
        }
        default { Write-Error "Unhandled error. Alerting administrators." }
    }
}
                    

Centralized Log Collection

Manually checking logs on individual servers doesn't scale. A scheduled task on each server can run a script to collect recent WinGet logs and copy them to a central file share for simplified auditing and fleet-wide analysis.


# Script to be run as a scheduled task on each server
$centralLogShare = "LogServer01WinGetLogs"
$serverName = $env:COMPUTERNAME
$destinationPath = Join-Path -Path $centralLogShare -ChildPath $serverName

# Find the WinGet log directory dynamically
$logDirectory = (winget --info | Select-String -Pattern "Logs").ToString().Split(' : ')[1].Trim()

# Find logs from the last 24 hours and copy them to the central share
Get-ChildItem -Path $logDirectory -Filter "*.log" | 
    Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-1) } |
    Copy-Item -Destination $destinationPath -Force
                    

6. Conclusion: A Holistic Management Strategy

The integration of WinGet into Windows Server 2025 is a transformative development. Mastering it requires a strategic approach grounded in best practices for automation, security, and operational resilience. A successful implementation hinges on standardizing on the PowerShell module for automation, leveraging Group Policy for security, implementing a tiered offline strategy, and building intelligent, idempotent scripts.

Ultimately, this WinGet-based automation framework is not an isolated solution. It is a powerful engine that can be integrated into a broader server management strategy, whether through Azure Arc, Microsoft Configuration Manager (SCCM), or third-party RMM tools. By adopting these tools, administrators can significantly reduce manual toil, improve their security posture, and build a server infrastructure that is more resilient, consistent, and prepared for the challenges of the future.

© 2025 GigXP.com. All rights reserved.

GigXP.com

Disclaimer: The Questions and Answers provided on https://gigxp.com are for general information purposes only. We make no representations or warranties of any kind, express or implied, about the completeness, accuracy, reliability, suitability or availability with respect to the website or the information, products, services, or related graphics contained on the website for any purpose.

What's your reaction?

Excited
0
Happy
0
In Love
0
Not Sure
0
Silly
0

Comments are closed.

More in:Tech Posts

Next Article:

0 %