Building a ZIP file from a folder containing shortcuts on Windows

While shortcuts can be handy, you don't generally want to include them in a compressed ZIP archive and would rather have the actual files that they point to. I couldn't find an obvious way to do this built in to Windows, so I ended up creating a PowerShell script.

Given a source directory such as the following:

.
├── Child1
│   └── Document1.docx.lnk
├── Child2
│   └── README.txt
└── Child3
    └── Document3.pdf.lnk

I needed to recurse all child folders and maintain the structure. I came up with the following PowerShell script to do just that:

Param (
    [Parameter(Mandatory = $True)]
    [string]
    $Source,

    [Parameter(Mandatory = $True)]
    [string]
    $Target,

    [array]
    $Exclude = @()
)

$ErrorActionPreference = 'Stop'

Function Create-Temporary-Directory {
    $parent = [System.IO.Path]::GetTempPath()
    [string] $name = [System.Guid]::NewGuid()
    $name = "${name}"

    $created = New-Item -Path $parent -Name $name -ItemType "directory" -Force

    Return $created.FullName
}

$tempDirectory = Create-Temporary-Directory

$timeTaken = Measure-Command {
    try {
        Copy-Item -Path "${Source}/*" -Destination "${tempDirectory}" -Recurse -Exclude $Exclude
    
        $sh = New-Object -ComObject WScript.Shell

        Get-ChildItem -Path "${tempDirectory}" -Filter "*.lnk" -Recurse | ForEach-Object {
            $shortcutPath = $_.FullName
            $shortcutTargetPath = $sh.CreateShortcut("${shortcutPath}").TargetPath

            $targetFolder = Split-Path -Path "${shortcutPath}" -Parent
            $targetFilename = Split-Path -Path "${shortcutPath}" -LeafBase
            $targetPath = Join-Path -Path "${targetFolder}" -ChildPath "${targetFilename}"

            Remove-Item -Path "${shortcutPath}"
            
            If (Test-Path -Path "${shortcutTargetPath}" -PathType Container) {
                Copy-Item -Path "${shortcutTargetPath}" -Destination "${targetPath}" -Recurse
            } else {
                Copy-Item -Path "${shortcutTargetPath}" -Destination "${targetPath}"
            }
        }

        Compress-Archive -Path "${tempDirectory}\*" -DestinationPath "${Target}" -Force
    }
    finally {
        Remove-Item -Path "${tempDirectory}" -Recurse -Force
    }
}

Write-Host "ZIP file created: ${Target}`nTime taken: ${timeTaken}"

The script has parameters for the source directory, the path to the target zip file, and an optional list of file names to exclude from the zip file.

.\publish.ps1 -Source $PWD -Target "$PWD/published.zip" -Exclude @("published.zip","publish.ps1")

Creating a zip file from the contents of the current working directory, and writing the zip file to the same working directory as "published.zip" while excluding both the script and the target zip file from being included.