How to create, update, and extract zip files with PowerShell

5 minute read

Lets take a look at working with zip files in PowerShell using .NET or the newer Archive module.

If you are going to be following along in PowerShell, here are examples of the variables I’m referring to with each snippet:

$zipFilePath = 'C:\Temp\zip.zip'
$toZipDirectory = 'C:\Temp\Zip'
$toZipDirectory2 = 'C:\Temp\Zip2'
$extractPath = 'C:\Temp\Extract'
$file1 = 'C:\Temp\File1.txt'
$file2 = 'C:\Temp\File2.txt'
$file3 = 'C:\Temp\File3.txt'

Pre PowerShell v5, .NET style

Before PowerShell v5, we had to rely on .NET to work with zip files. For these examples, I will be using .NET 4.5.

Before you begin, add the type to your session:

Add-Type -Assembly 'System.IO.Compression.FileSystem'

Create a .zip file

There are two notable ways to create .zip files with .NET. The first is from a directory using the CreateFromDirectory() method. You give it the directory you want to zip, then the path of the .zip file that you want to create:

[System.IO.Compression.ZipFile]::CreateFromDirectory($toZipDirectory, $zipFilePath)

You can verify the contents using the OpenRead() method:

[System.IO.Compression.ZipFile]::OpenRead($zipFilePath).Entries.Name

The other way I want to mention is by using the Open() method. This method requires 2 parameters, the first is the zip file that you are ‘opening’ and the second is the ‘mode’ to open it with. It accepts ‘create’, ‘update’, and ‘read’. Don’t worry, we’ll check out all of them!

Here we are using the ‘create’ mode to create an empty .zip file:

$zip = [System.IO.Compression.ZipFile]::Open($zipFilePath, 'create')

And always make sure to close the locks on that file like so:

$zip.Dispose()

Update a .zip file

We can add files to a .zip file using .NET, but first we should define a compression level for the files:

$compressionLevel = [System.IO.Compression.CompressionLevel]::Fastest

We can specify: Fastest, NoCompression, or Optimal.

Then, lets open one of the .zip files for updating using the ‘update’ mode:

$zip = [System.IO.Compression.ZipFile]::Open($zipFilePath, 'update')

And then add a file to it:

[System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($zip, $file, (Split-Path $file1 -Leaf), $compressionLevel)

The third parameter in the CreateEntryFromFile() method is asking for the display name of the .zip file and I’m using Split-Path to pass the name of the file itself.

Bulk update a .zip file

We can, of course, bulk add files to a .zip file as well! This uses the same .zip file we opened for updating and the earlier defined compression level:

Get-ChildItem $FilesDirectory | ForEach-Object {[System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($zip, $_.FullName, (Split-Path $_.FullName -Leaf), $compressionLevel)}

And don’t forget to close out that .zip file:

$zip.Dispose()

Extract a .zip file

To extract a .zip file using .NET, we must first open it for reading (told you we’d use all of the modes!):

$zip = [System.IO.Compression.ZipFile]::Open($zipFilePath, 'read')

And then we can use the ExtractToDirectory() method, giving it the .zip file we just opened and the path to extract it to:

[System.IO.Compression.ZipFileExtensions]::ExtractToDirectory($zip, $extractPath)

To verify the extraction, we can use Get-ChildItem:

Get-ChildItem $extractPath

To only extract a single file from a .zip file, things get a little trickier. We need to specify the ‘entry’ object within the .zip file object and pass that to the ExtractToFile() method.

Here, we are extracting the first file in our .zip file by referencing the first item in the .Entries array, naming it ‘ExtractedFile1.txt’, and then specifying $true for overwrite.

[System.IO.Compression.ZipFileExtensions]::ExtractToFile($zip.Entries[0], "$extractPath\ExtractedFile1.txt", $true)

That gets tedious doing it for every file, so lets use the pipeline! We can filter using the Where-Object cmdlet. In this example, we are going to extract all .txt files from that .zip file:

$zip.Entries | Where-Object Name -like *.txt | ForEach-Object{[System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, "$extractPath\$($_.Name)", $true)}

Or maybe we want all files that were modified in the last month:

$zip.Entries | Where-Object {$_.LastWriteTime.DateTime -gt (Get-Date).AddMonths(-1)} | ForEach-Object{[System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, "$extractPath\$($_.Name)", $true)}

There is a lot of things you can do with that.

And don’t forget to close out the .zip file of course:

$zip.Dispose()

PowerShell v5, module style

In PowerShell v5, we have the Microsoft.PowerShell.Archive module that we can take advantage of! No more .NET! Well, no more overt .NET anyway.

Create a .zip file

We now have the convenience of using a nice easy Compress-Archive cmdlet to create a .zip file either from a directory or from a file.

Using a directory:

Compress-Archive -Path $toZipDirectory -DestinationPath $zipFilePath

Using a file, or in this case, an array of files. Each fully qualified name separated by a comma:

Compress-Archive -Path $file1,$file2 -DestinationPath $zipFilePath

Update a .zip file

We use the same Compress-Archive cmdlet to update a .zip file as well, but now we need to add the -Update switch.

To update the .zip file with an additional directory:

Compress-Archive -Path $toZipDirectory2 -DestinationPath $zipFilePath -Update

Or to add in another file to the .zip file:

Compress-Archive -Path $file3 -DestinationPath $zipFilePath -Update

Extract a .zip file

To extract a .zip file, the archive module has the Expand-Archive cmdlet. The only thing it needs is the path of the .zip file and the extraction path:

Expand-Archive -Path $zipFilePath -DestinationPath $extractPath

Unfortunately there is no way (that I know of) to use the Expand-Archive cmdlet and specify only certain files for extraction.

Leave a Comment