Run Chocolatey Commands in PowerShell Scripts
Integrating powerful command-line tools into scripting languages is a cornerstone of efficient system administration and automation. When it comes to Windows software management, Chocolatey is the de facto standard for package management via the command line. And for Windows scripting, PowerShell is the go-to language. Combining these two allows you to build robust, automated workflows. This post will guide you on how to effectively run Chocolatey commands in PowerShell scripts, unlocking advanced automation capabilities.
Why Integrate choco
into PowerShell?
While you can run basic choco
commands directly in a CMD or PowerShell window, integrating them into a PowerShell script offers significant advantages:
- Power & Flexibility: Use PowerShell’s full scripting logic – variables, loops, conditionals (
if/else
), arrays – around your Chocolatey commands. - Advanced Automation: Perform complex pre-installation checks (e.g., check disk space, verify prerequisites), handle post-installation configurations (set registry keys, modify files), and manage errors gracefully.
- System State Management: Combine software installation and upgrades with other system configuration tasks like configuring services, setting environment variables, or managing firewall rules, all within a single script.
- Better Error Handling: Leverage PowerShell’s structured error handling mechanisms (like
try/catch
) which are far more powerful than simple batch file error checking. - Centralized Scripts: Create single, executable
.ps1
files that can fully configure a machine or deploy an application stack, making your setup repeatable and consistent.
Prerequisites
Before you start writing scripts, ensure you have the following:
- Chocolatey CLI Installed: Make sure Chocolatey is installed and working correctly on your system. If not, follow the official installation guide.
- Basic PowerShell Knowledge: This guide assumes you have a fundamental understanding of PowerShell concepts like variables, commands, and scripts.
- PowerShell Execution Policy: Your PowerShell execution policy must be configured to allow running scripts. The Chocolatey installation guide typically covers this, or you can refer to Microsoft’s documentation on Execution Policies.
Running choco
Commands in PowerShell (The Basics)
Executing external commands like choco
within a PowerShell script is straightforward. You simply type the command name followed by its arguments, just as you would in a regular PowerShell console.
For example, to install Google Chrome silently, you would use:
choco install googlechrome -y
In a PowerShell script (a .ps1
file), this line would look exactly the same:
# My first Chocolatey PowerShell script
Write-Host "Starting software installation..."
# Install Google Chrome silently
choco install googlechrome -y
Write-Host "Installation command sent for Google Chrome."
Write-Host
is a standard PowerShell cmdlet used to display output to the console, helping you track the script’s progress.
Crucial Note: Administrator Privileges
Just like running choco install
, choco upgrade
, choco uninstall
, or commands that modify sources directly in the console requires Administrator privileges, so does running a PowerShell script that contains these commands. You must launch the PowerShell session where you run the script, or the script itself, “As Administrator”.
To run a script as Administrator, right-click the .ps1
file and select “Run as administrator”. Alternatively, launch PowerShell as Administrator first, and then navigate to and run your script (e.g., .\your_script_name.ps1
).
While PowerShell’s Start-Process -Verb RunAs
can launch a command with elevated privileges *from* a non-elevated script, it typically opens a new window and makes capturing output or checking exit codes difficult for the elevated process. For most simple setup scripts, it’s far easier and more reliable to just ensure the entire script is run from an elevated PowerShell session.
Capturing Command Output and Exit Codes
In automation, simply running a command isn’t enough. You need to know if it succeeded or failed. External commands like choco
communicate their success or failure status back to the operating system using an “exit code”.
Exit Codes ($LASTEXITCODE
)
By convention, an exit code of 0
indicates success, and any non-zero value indicates some form of failure or warning. After any external command finishes executing in PowerShell, its exit code is stored in the automatic variable $LASTEXITCODE
. This is the most common way to check if a choco
command worked.
Here’s how you can check the exit code after installing a package:
Write-Host "Attempting to install 7-Zip..."
# Run the choco install command
choco install 7zip -y
# Check the exit code immediately after the command
if ($LASTEXITCODE -ne 0) {
# If $LASTEXITCODE is not 0, something went wrong
Write-Error "7-Zip installation failed with exit code $LASTEXITCODE"
# You might want to stop the script here or take other action
# exit 1 # Example: exit the script with a non-zero code indicating failure
} else {
# If $LASTEXITCODE is 0, the command reported success
Write-Host "7-Zip installed successfully (or already installed)."
}
Write-Host "Script continues after 7-Zip check."
This code snippet first attempts to install 7-Zip. Then, it checks the value of $LASTEXITCODE
. If it’s anything other than 0, it writes an error message including the specific exit code returned by choco
. Otherwise, it confirms success.
Capturing Text Output
Sometimes, you might need to capture the text that choco
prints to the console (both standard output and error messages) to parse it or log it. You can do this by assigning the command’s output to a variable:
Write-Host "Attempting to install VLC media player and capturing output..."
# Run the command and capture all output (stdout and stderr) into a variable
$chocoOutput = choco install vlc -y
# Now $chocoOutput contains the text that was printed to the console
Write-Host "Chocolatey output for VLC installation:"
Write-Host "--- Start Output ---"
Write-Host $chocoOutput
Write-Host "--- End Output ---"
# You can still check the exit code as well
if ($LASTEXITCODE -ne 0) {
Write-Error "VLC installation failed with exit code $LASTEXITCODE."
# You could also search $chocoOutput for specific error messages here
} else {
Write-Host "VLC installation command finished (check output above for details)."
}
Assigning the command execution directly to a variable captures the output. This captured text can be useful for debugging or for scripts that need to react based on specific messages printed by choco
.
Handling Errors Gracefully (try/catch
)
While checking $LASTEXITCODE
is essential, PowerShell offers more sophisticated error handling using try
and catch
blocks. This allows you to define a block of code to attempt (try
) and a separate block to execute if an error occurs within the try
block (catch
).
By default, non-zero exit codes from external commands like choco
do *not* cause a terminating error in PowerShell that a standard catch
block would intercept. To make PowerShell treat a non-zero exit code as an error that can be caught, you typically need to set the $ErrorActionPreference
variable to 'Stop'
*before* the command, or use the -ErrorAction Stop
common parameter on the command itself (if it supports it, which external commands don’t in the standard way, but PowerShell can be configured to treat them this way). A common pattern is to explicitly check $LASTEXITCODE
inside the try
block and use throw
if it’s non-zero, which *will* trigger the catch
block.
Here’s an example demonstrating a more robust approach using try/catch
and checking $LASTEXITCODE
:
# Set ErrorActionPreference to Continue so script doesn't stop on non-terminating errors
# This is often the default, but good to be explicit
$ErrorActionPreference = 'Continue'try {
Write-Host "Attempting to install a specific version of MyApp..."
# Execute the choco command
# We don't use -ErrorAction Stop directly on choco, as we'll check $LASTEXITCODE
choco install MyApp --version 1.2.3 -y
# Explicitly check the exit code returned by choco
if ($LASTEXITCODE -ne 0) {
# If choco returned non-zero, create a PowerShell error using throw
throw "Chocolatey installation of MyApp failed with exit code $LASTEXITCODE."
}
# If we reach here, choco returned 0
Write-Host "MyApp installed successfully (or already installed)."
} catch {
# This block executes if a terminating error occurred in the try block,
# including the error we threw explicitly.
Write-Error "An error occurred during MyApp installation:"
Write-Error $_.Exception.Message # $_ is the current error object
# --- Error Handling Actions ---
# You can add logic here to:
# - Log the error to a file
# - Send an email notification
# - Attempt a different installation method
# - Clean up failed installation attempts
# - Decide whether to continue the script or exit
# Example: Decide to exit the script if this critical installation fails
# exit 1
# Exit the script with a non-zero code
Write-Host "Decided to continue script despite MyApp installation failure."
}
Write-Host "Script continues after the try/catch block."
In this example:
- The code that might fail (the
choco install
command) is inside thetry
block. - We immediately check
$LASTEXITCODE
after thechoco
command. - If
$LASTEXITCODE
is not 0, we usethrow
to generate a terminating PowerShell error. - The
catch
block catches this error (or any other terminating error that might occur in thetry
). - Inside the
catch
block,$_
represents the error object, and$_.Exception.Message
gives you a description of the error. - You can place your specific error handling logic within the
catch
block.
This pattern provides much better control over your script’s flow when a choco
command fails, allowing you to implement custom recovery or reporting mechanisms.
Using Chocolatey within PowerShell Functions
As your scripts grow, you’ll find yourself repeating patterns, like installing a package and checking its exit code. PowerShell functions allow you to encapsulate this logic into reusable blocks, making your scripts cleaner, more organized, and easier to maintain.
Here’s an example of a simple function to install a Chocolatey package, incorporating the exit code check:
# Define a function to install a Chocolatey package
function Install-ChocolateyPackage {
param(
# Define parameters the function accepts
[Parameter(Mandatory=$true)]
[string]$PackageId, # The ID of the package to install
[Parameter(Mandatory=$false)]
[string]$Version, # Optional: Specify a version
[Parameter(Mandatory=$false)]
[switch]$Force # Optional: Add the --force flag
)
Write-Host "Attempting to install package: $PackageId..."
# Build the arguments string for the choco command
$installArgs = "$PackageId -y"
if ($Version) { $installArgs += " --version $Version" }
if ($Force) { $installArgs += " --force" }
# Execute the choco command using the built arguments
# We are relying on the $LASTEXITCODE check after the command
choco install $installArgs
# Check the exit code returned by choco
if ($LASTEXITCODE -ne 0) {
# Write a PowerShell error
Write-Error "Installation of $PackageId failed with exit code $LASTEXITCODE."
# Optionally, you could throw an error here to be caught by the caller's try/catch
# throw "InstallFailed"
# Or return a status indicator
# return $false
} else {
Write-Host "Installation of $PackageId successful (or already installed)."
# Optionally return a status indicator
# return $true
}
}
# --- Example usage of the function in your script ---
# Install a package using the function
Install-ChocolateyPackage -PackageId "googlechrome"
# Install another package, specifying a version
Install-ChocolateyPackage -PackageId "vscode" -Version "1.80.0"
# Install a package using the force flag
Install-ChocolateyPackage -PackageId "notepadplusplus" -Force
Write-Host "Script finished attempting package installations."
This function takes parameters for the package ID, an optional version, and an optional force flag. It constructs the appropriate choco install
command, runs it, and checks $LASTEXITCODE
, reporting success or failure. Using functions like this makes your main script logic much cleaner, consisting mostly of calls to your reusable functions.
Advanced Concepts & Further Automation
Integrating choco
into PowerShell scripts opens the door to more sophisticated automation:
- Checking for Updates: Use
choco outdated
within a script to see which packages have updates available before performing upgrades. - Installing Package Lists: Read a list of package IDs from a text file using
Get-Content
and loop through the list, calling yourInstall-ChocolateyPackage
function for each one. - Combining with System Configuration: After installing software, use other PowerShell cmdlets to configure that software, set environment variables, or configure Windows features.
- Desired State Configuration (DSC): For enterprise environments, Chocolatey integrates with PowerShell DSC, allowing you to declare the desired state of software on your systems, and DSC handles the enforcement. This is a more advanced configuration management approach.
Conclusion
By integrating choco
commands directly into your Chocolatey PowerShell scripts, you move beyond basic package installation to powerful, automated system setup and management. Leveraging PowerShell’s logic, variables, error handling ($LASTEXITCODE
, try/catch
), and functions allows you to build robust, reusable scripts that can handle complex installation scenarios and ensure your systems are configured exactly as you need them. Start by taking a simple setup task and scripting it, gradually incorporating error checks and functions as you become more comfortable.