Skip to content

Instantly share code, notes, and snippets.

@SQLDBAWithABeard
Last active January 8, 2026 12:17
Show Gist options
  • Select an option

  • Save SQLDBAWithABeard/fc2c5bf1e0c2ba6a89e88d234e1a79c0 to your computer and use it in GitHub Desktop.

Select an option

Save SQLDBAWithABeard/fc2c5bf1e0c2ba6a89e88d234e1a79c0 to your computer and use it in GitHub Desktop.
UnZipJustSubDir
function Expand-ZipSubdirectory {
<#
.SYNOPSIS
Extracts only a specific subdirectory from a ZIP archive.
.DESCRIPTION
Expand-ZipSubdirectory allows selective extraction of a single subdirectory
from a ZIP file. Unlike Expand-Archive, which extracts the entire archive,
this function filters ZIP entries by path and extracts only the matching
subtree.
The function validates:
- The ZIP file exists and has a .zip extension
- The destination directory exists or can be created
- The subdirectory parameter is not empty
ZIP archives always use forward slashes internally. The function normalizes
the provided subdirectory path automatically.
.PARAMETER ZipPath
The full path to the .zip file. Must exist and must have a .zip extension.
.PARAMETER Subdirectory
The internal ZIP subdirectory to extract. This should be the folder path
inside the ZIP (e.g., "module/MicrosoftFabricMgmt").
.PARAMETER Destination
The directory where the extracted files will be placed. If it does not exist,
the function attempts to create it.
.PARAMETER Force
If specified, existing files in the destination will be overwritten.
.EXAMPLE
Expand-ZipSubdirectory -ZipPath "C:\temp\package.zip" `
-Subdirectory "module/MicrosoftFabricMgmt" `
-Destination "C:\temp\extract"
Extracts only the MicrosoftFabricMgmt subdirectory from the ZIP.
.EXAMPLE
Expand-ZipSubdirectory -ZipPath ".\build.zip" `
-Subdirectory "output/bin" `
-Destination ".\bin" `
-Force
Extracts the "output/bin" folder and overwrites existing files.
.NOTES
This function uses System.IO.Compression.ZipFile from .NET and does not rely
on Expand-Archive. It supports nested directories and preserves structure.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[ValidateScript({
if (-not (Test-Path $_)) {
throw "ZipPath '$_' does not exist."
}
if (-not ($_ -match '\.zip$')) {
throw "ZipPath '$_' is not a .zip file."
}
$true
})]
[string]$ZipPath,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$Subdirectory,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[ValidateScript({
if (-not (Test-Path $_)) {
try {
New-Item -ItemType Directory -Path $_ -ErrorAction Stop | Out-Null
}
catch {
throw "Destination '$_' does not exist and could not be created."
}
}
$true
})]
[string]$Destination,
[switch]$Force
)
# Normalize ZIP internal path format
$normalized = $Subdirectory.TrimStart('/','\') -replace '\\','/'
if (-not $normalized.EndsWith('/')) {
$normalized += '/'
}
Add-Type -AssemblyName System.IO.Compression.FileSystem
$zip = [System.IO.Compression.ZipFile]::OpenRead($ZipPath)
try {
$matched = $false
foreach ($entry in $zip.Entries) {
if ($entry.FullName.StartsWith($normalized, [StringComparison]::OrdinalIgnoreCase)) {
$matched = $true
$relative = $entry.FullName.Substring($normalized.Length)
if (-not $relative) { continue }
$target = Join-Path $Destination $relative
$targetDir = Split-Path $target
if (-not (Test-Path $targetDir)) {
New-Item -ItemType Directory -Path $targetDir | Out-Null
}
if ($entry.Name) {
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, $target, $Force.IsPresent)
}
}
}
if (-not $matched) {
throw "Subdirectory '$Subdirectory' was not found inside the ZIP."
}
}
finally {
$zip.Dispose()
}
}
I am big! It's the pictures that got small.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment