How to Zip and Unzip Files with PowerShell

📜Part of the PowerShell Complete Guide, every PowerShell post on this site, grouped by the job you are doing.
The short answer

Compress-Archive to zip, Expand-Archive to unzip. Both built in since PowerShell 5, no extra module needed.

Before PowerShell 5 this meant .NET calls or a third-party tool. Now it is two cmdlets, and they are the right answer for almost every scripted archive job on Windows.

Zip

# zip the contents of a folder
Compress-Archive -Path "D:\Logs\*" -DestinationPath "D:\Archive\logs.zip"

# overwrite an existing zip
Compress-Archive -Path "D:\Logs\*" -DestinationPath "D:\Archive\logs.zip" -Force

# add to an existing zip instead of replacing it
Compress-Archive -Path "D:\Logs\extra.log" -DestinationPath "D:\Archive\logs.zip" -Update

# a dated archive, which is what most scheduled jobs actually want
$stamp = Get-Date -Format 'yyyyMMdd'
Compress-Archive -Path "D:\Logs\*.log" -DestinationPath "D:\Archive\logs-$stamp.zip"

Unzip

Expand-Archive -Path "D:\Archive\logs.zip" -DestinationPath "D:\Restored"

# overwrite files that are already there
Expand-Archive -Path "D:\Archive\logs.zip" -DestinationPath "D:\Restored" -Force

Leave -DestinationPath off and it extracts into a folder named after the zip, in the current directory.

The Trap: Path vs Path\*

This is the one that surprises people. -Path "D:\Logs" puts the folder inside the zip, so everything ends up nested under Logs\. -Path "D:\Logs\*" puts the folder contents at the root of the zip. Neither is wrong, but they are different, and you usually want the second.

Compress-Archive has a 2GB limit in Windows PowerShell 5.1 because of the underlying API. PowerShell 7 handles larger archives. If you are archiving anything substantial on 5.1, either split it or use .NET’s ZipFile class directly.

Selective Archiving

Combine it with a pipeline when you want to archive only what matches:

# zip only logs older than 30 days, then delete the originals
$old = Get-ChildItem "D:\Logs" -Filter *.log |
       Where-Object LastWriteTime -lt (Get-Date).AddDays(-30)

if ($old) {
    $stamp = Get-Date -Format 'yyyyMMdd'
    Compress-Archive -Path $old.FullName -DestinationPath "D:\Archive\old-$stamp.zip"
    $old | Remove-Item -WhatIf
}

Related


If SQL Server is part of your day job, my current work lives over at sqldba.blog: production DBA scripts, an error library and the SSMS guide.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

Looking for SQL Server?

This site started as a SQL Server blog in 2017 and the archive is still here. The new SQL Server writing, and all the scripts, moved to a site of their own.

sqldba.blogScripts, error library, wait types, SSMS
Browse by topic