How to Configure Chocolatey for Automated Package Management on Windows Server 2025
Chocolatey is the de facto standard package manager for Windows enterprise automation. Where Linux administrators reach for apt or yum, Windows administrators building repeatable, auditable server environments reach for Chocolatey. On Windows Server 2025, Chocolatey integrates seamlessly with PowerShell Desired State Configuration (DSC), Group Policy-based deployment pipelines, and internal artifact repositories like Nexus or ProGet, allowing you to move from “click next to install” to fully scripted, version-controlled software provisioning. This tutorial covers Chocolatey in an enterprise context: integrating it with PowerShell DSC, creating custom packages for in-house software, building an internal feed, configuring machines to consume that feed, and bootstrapping entire server fleets with a single script.
Prerequisites
- Windows Server 2025 with PowerShell 7.4 or later
- Internet access or access to an internal Chocolatey feed
- Administrator rights on all target machines
- PowerShell execution policy set to at least
RemoteSigned:Set-ExecutionPolicy RemoteSigned -Scope LocalMachine - Nexus Repository OSS (or ProGet) if hosting an internal feed — a separate server is recommended
- Chocolatey for Business (C4B) licence if using advanced features (package internalizer, self-service); Chocolatey Open Source is free and sufficient for most scenarios covered here
Step 1: Installing Chocolatey
The standard Chocolatey installation uses a one-line PowerShell command that downloads and runs the installation script from chocolatey.org. For air-gapped or security-conscious environments, download the Chocolatey NuGet package (chocolatey.x.y.z.nupkg) offline, copy it to the target server, and bootstrap from that local file.
# Standard installation (requires internet access)
Set-ExecutionPolicy Bypass -Scope Process -Force
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
# Verify
choco --version
# --- Offline installation (air-gapped) ---
# 1. On an internet-connected machine, download the nupkg:
# Invoke-WebRequest 'https://community.chocolatey.org/api/v2/package/chocolatey' -OutFile chocolatey.nupkg
# 2. Copy chocolatey.nupkg to the server
# 3. Run on the server:
$nupkgPath = 'C:installchocolatey.nupkg'
$env:ChocolateyInstall = 'C:ProgramDatachocolatey'
New-Item -ItemType Directory -Path $env:ChocolateyInstall -Force | Out-Null
# Extract the nupkg (it is a ZIP) and run the embedded install script
Add-Type -AssemblyName System.IO.Compression.FileSystem
[IO.Compression.ZipFile]::ExtractToDirectory($nupkgPath, "$env:TEMPchoco_extract")
& "$env:TEMPchoco_extracttoolschocolateyInstall.ps1"
Step 2: Using Chocolatey with PowerShell DSC
PowerShell Desired State Configuration enforces a server’s software state — if a package drifts from the desired version, DSC automatically corrects it on the next consistency check. The cChoco DSC resource module provides a cChocoInstaller resource (to ensure Chocolatey itself is installed) and a cChocoPackageInstaller resource (to manage packages).
# Install the cChoco DSC resource module
Install-Module -Name cChoco -Scope AllUsers -Force
# Verify
Get-DscResource -Module cChoco
# WebServerConfiguration.ps1 — DSC configuration for a web server
Configuration WebServerBaseline {
param (
[string[]]$ComputerName = 'localhost'
)
Import-DscResource -ModuleName 'PSDesiredStateConfiguration'
Import-DscResource -ModuleName 'cChoco'
Node $ComputerName {
# Ensure Chocolatey is installed first
cChocoInstaller installChoco {
InstallDir = 'C:ProgramDatachocolatey'
}
# Point Chocolatey at the internal feed
cChocoSource internalFeed {
Ensure = 'Present'
Name = 'internal'
Source = 'https://nexus.example.com/repository/nuget-choco/index.json'
Priority = 1
DependsOn = '[cChocoInstaller]installChoco'
}
# Remove the public community feed in locked-down environments
cChocoSource communityFeed {
Ensure = 'Absent'
Name = 'chocolatey'
DependsOn = '[cChocoInstaller]installChoco'
}
# Install packages
cChocoPackageInstaller installNotepadPP {
Name = 'notepadplusplus'
Version = '8.7.1'
Ensure = 'Present'
DependsOn = '[cChocoSource]internalFeed'
}
cChocoPackageInstaller installGit {
Name = 'git'
Version = '2.47.1'
Ensure = 'Present'
Params = "'/GitAndUnixToolsOnPath /WindowsTerminal'"
DependsOn = '[cChocoSource]internalFeed'
}
cChocoPackageInstaller installDotNet {
Name = 'dotnet-8.0-runtime'
Ensure = 'Present'
DependsOn = '[cChocoSource]internalFeed'
}
}
}
# Compile the MOF
WebServerBaseline -ComputerName @('webserver01', 'webserver02') -OutputPath 'C:DSCWebServerBaseline'
# Apply to local machine
Start-DscConfiguration -Path 'C:DSCWebServerBaseline' -Wait -Verbose -Force
Step 3: Creating a Custom Chocolatey Package
Chocolatey packages are NuGet packages with a .nuspec manifest and a tools folder containing PowerShell scripts. The choco new command scaffolds the directory structure for you.
# Scaffold a new package
choco new mycompany-internalapp
# This creates:
# mycompany-internalapp
# mycompany-internalapp.nuspec
# tools
# chocolateyInstall.ps1
# chocolateyUninstall.ps1 (optional)
# chocolateyBeforeModify.ps1 (optional)
Edit the generated .nuspec to fill in version, description, and dependencies:
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd">
<metadata>
<id>mycompany-internalapp</id>
<version>3.5.1</version>
<title>MyCompany Internal App</title>
<authors>MyCompany IT</authors>
<description>Internal line-of-business application for MyCompany.</description>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<tags>mycompany internal business</tags>
<dependencies>
<dependency id="dotnet-8.0-runtime" version="8.0.0" />
</dependencies>
</metadata>
</package>
Write the install script in toolschocolateyInstall.ps1:
# toolschocolateyInstall.ps1
$ErrorActionPreference = 'Stop'
$packageArgs = @{
packageName = $env:ChocolateyPackageName
fileType = 'exe'
# Installer can be embedded in the package or fetched from an internal URL
url64bit = 'https://artifacts.example.com/internalapp/3.5.1/InternalApp-3.5.1-x64.exe'
checksum64 = 'ABC123DEF456...' # SHA256 of the installer EXE
checksumType64 = 'sha256'
silentArgs = '/S /D=C:Program FilesMyCompanyInternalApp'
validExitCodes = @(0, 3010) # 3010 = reboot required but not enforced
}
Install-ChocolateyPackage @packageArgs
Step 4: Building and Publishing the Package
Once the .nuspec and install scripts are ready, compile the package into a .nupkg file and push it to your internal feed.
# Build the package (run from the package root directory)
Set-Location 'C:choco-packagesmycompany-internalapp'
choco pack
# Resulting file: mycompany-internalapp.3.5.1.nupkg
# Push to Nexus Repository (NuGet feed)
# The API key is configured in Nexus and stored securely
$nexusApiKey = (Get-Content 'C:Secretsnexus_apikey.txt' -Raw).Trim()
$feedUrl = 'https://nexus.example.com/repository/nuget-choco/'
choco push mycompany-internalapp.3.5.1.nupkg --source "$feedUrl" --api-key $nexusApiKey
# Verify it appeared in the feed
choco search mycompany-internalapp --source "$feedUrl"
Step 5: Setting Up an Internal Chocolatey Feed with Nexus Repository
Nexus Repository OSS (free) or ProGet (commercial) both support the NuGet v2 feed protocol that Chocolatey uses. On a Nexus server, create a hosted NuGet repository and note its repository URL. Then configure all target machines to use it as their primary Chocolatey source.
# On each target machine — add the internal feed and remove the public one
$internalFeedUrl = 'https://nexus.example.com/repository/nuget-choco/index.json'
# Add internal feed with highest priority (lower number = higher priority)
choco source add --name 'internal' --source $internalFeedUrl --priority 1
# Optionally provide credentials if the Nexus feed requires authentication
choco source add --name 'internal' `
--source $internalFeedUrl `
--user 'choco-reader' `
--password 'ReaderPassword!' `
--priority 1
# Disable the public Chocolatey community feed
choco source disable --name 'chocolatey'
# List configured sources
choco source list
Step 6: Bulk Machine Provisioning with a Bootstrap Script
Combine everything above into a bootstrap script that you deploy via Group Policy startup script, a WDS/MDT task sequence, or a remote PowerShell session. This script installs Chocolatey, points it at the internal feed, and installs the standard software baseline — turning a freshly installed Windows Server 2025 into a fully provisioned machine in minutes.
#Requires -RunAsAdministrator
# bootstrap-server.ps1 — Run once on new Windows Server 2025 machines
param(
[string]$Role = 'base', # base | webserver | dbserver | devtools
[string]$InternalFeed = 'https://nexus.example.com/repository/nuget-choco/index.json',
[string]$FeedUser = 'choco-reader',
[string]$FeedPassword = 'ReaderPassword!'
)
Set-ExecutionPolicy Bypass -Scope Process -Force
# Step 1: Install Chocolatey
if (-not (Get-Command choco -ErrorAction SilentlyContinue)) {
Write-Host "Installing Chocolatey..."
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor 3072
Invoke-Expression ((New-Object Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
}
# Step 2: Configure feed
choco source add --name 'internal' --source $InternalFeed `
--user $FeedUser --password $FeedPassword --priority 1
choco source disable --name 'chocolatey'
# Step 3: Install baseline packages
$basePackages = @('git', 'notepadplusplus', '7zip', 'curl', 'powershell-core')
$rolePackages = @{
base = @()
webserver = @('dotnet-8.0-aspnetcore-runtime', 'urlrewrite', 'webdeploy')
dbserver = @('sql-server-management-studio')
devtools = @('vscode', 'nodejs-lts', 'python312', 'docker-desktop')
}
$allPackages = $basePackages + $rolePackages[$Role]
foreach ($pkg in $allPackages) {
Write-Host "Installing: $pkg"
choco install $pkg --yes --no-progress --source 'internal'
if ($LASTEXITCODE -notin @(0, 3010)) {
Write-Warning "Package '$pkg' exited with code $LASTEXITCODE"
}
}
Write-Host "`nBootstrap complete. Role: $Role"
choco list
Chocolatey transforms Windows Server 2025 software management from a manual, error-prone process into a fully automated, version-controlled pipeline. By combining Chocolatey with PowerShell DSC you get drift correction; by hosting your own Nexus or ProGet feed you get air-gap capability and the ability to vet every package before it reaches production; and by packaging your own internal applications as Chocolatey packages you gain the same single-command install experience for bespoke software that you get for commercial and open-source tools. The result is a Windows Server fleet where every machine can be rebuilt to a known good state in minutes, with a complete audit trail of what is installed at what version on each node.