PowerShell – Copy and remove old files for installation for MECMcache

Below is the updated PowerShell 5.1 script that accepts -AppName, -Version, and -SourceFile, unblocks the source file before copying it to C:\Windows\_MECMCache\$AppName\$Version, creates directories as needed, removes version folders older than 60 days, and logs actions and errors to a file and the Windows Application event log.
<#
.SYNOPSIS
Copy a file into MECM cache, clean old versions, unblock before copy, and log events.
.DESCRIPTION
Accepts parameters -AppName, -Version, -SourceFile.
If -SourceFile is not provided, the script looks for "fileA" in the script root.
Requires PowerShell 5.1. Run as Administrator to create event source and write to C:\Windows.
.EXAMPLE
.\Install-Cache.ps1 -AppName App1 -Version 1.0.0.0 -SourceFile "C:\temp\fileA.exe"
#>
param(
[Parameter(Mandatory=$true)]
[string]$AppName,
[Parameter(Mandatory=$true)]
[string]$Version,
[Parameter(Mandatory=$false)]
[string]$SourceFile
)
# === Configuration ===
$cacheRoot = "C:\Windows\_MECMCache"
$daysToKeep = 60
$defaultFileName = "fileA"
# === Derived paths ===
$destRoot = Join-Path -Path $cacheRoot -ChildPath $AppName
$destVersionPath = Join-Path -Path $destRoot -ChildPath $Version
$logDir = Join-Path -Path $destRoot -ChildPath "Logs"
$logFile = Join-Path -Path $logDir -ChildPath "install.log"
$eventSource = "MECMCacheInstaller-$AppName"
# === Helper functions ===
function Ensure-LogDirectory {
try {
if (-not (Test-Path -Path $logDir)) {
New-Item -Path $logDir -ItemType Directory -Force | Out-Null
}
} catch {
Write-Host "Failed to create log directory $logDir : $($_.Exception.Message)" -ForegroundColor Red
}
}
function Write-Log {
param(
[string]$Level,
[string]$Message
)
$timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
$line = "$timestamp`t[$Level]`t$Message"
try {
Ensure-LogDirectory
Add-Content -Path $logFile -Value $line -ErrorAction Stop
} catch {
Write-Host "Unable to write to log file $logFile : $($_.Exception.Message)" -ForegroundColor Yellow
}
try {
if (-not [System.Diagnostics.EventLog]::SourceExists($eventSource)) {
New-EventLog -LogName Application -Source $eventSource
}
switch ($Level.ToUpper()) {
"ERROR" { Write-EventLog -LogName Application -Source $eventSource -EntryType Error -EventId 1001 -Message $Message }
"WARNING" { Write-EventLog -LogName Application -Source $eventSource -EntryType Warning -EventId 1002 -Message $Message }
default { Write-EventLog -LogName Application -Source $eventSource -EntryType Information -EventId 1000 -Message $Message }
}
} catch {
$errMsg = "EventLog write failed: $($_.Exception.Message)"
try { Add-Content -Path $logFile -Value "$timestamp`t[WARN]`t$errMsg" } catch {}
}
}
function Write-Info { param($m) Write-Log -Level "INFO" -Message $m }
function Write-Warn { param($m) Write-Log -Level "WARNING" -Message $m }
function Write-ErrorLog { param($m) Write-Log -Level "ERROR" -Message $m }
# === Resolve source path ===
try {
if ($SourceFile) {
if (Test-Path -Path $SourceFile -PathType Leaf) {
$sourcePath = (Resolve-Path -Path $SourceFile).ProviderPath
} else {
if ($PSScriptRoot) {
$candidate = Join-Path -Path $PSScriptRoot -ChildPath $SourceFile
if (Test-Path -Path $candidate -PathType Leaf) {
$sourcePath = (Resolve-Path -Path $candidate).ProviderPath
} else {
throw "SourceFile specified but not found: $SourceFile"
}
} else {
throw "SourceFile specified but not found and script root is unavailable: $SourceFile"
}
}
} else {
if ($PSScriptRoot) {
$candidate = Join-Path -Path $PSScriptRoot -ChildPath $defaultFileName
if (Test-Path -Path $candidate -PathType Leaf) {
$sourcePath = (Resolve-Path -Path $candidate).ProviderPath
} else {
throw "No SourceFile provided and default file not found in script root: $candidate"
}
} else {
throw "No SourceFile provided and script root is unavailable."
}
}
Write-Info "Resolved source file: $sourcePath"
} catch {
$msg = "Source resolution failed: $($_.Exception.Message)"
Write-ErrorLog $msg
Write-Host $msg -ForegroundColor Red
exit 2
}
# === Unblock source file before copying ===
try {
# Only attempt Unblock-File if the cmdlet exists (PowerShell 3+)
if (Get-Command -Name Unblock-File -ErrorAction SilentlyContinue) {
try {
Unblock-File -Path $sourcePath -ErrorAction Stop
Write-Info "Unblocked source file: $sourcePath"
} catch {
$uMsg = "Failed to unblock file $sourcePath : $($_.Exception.Message)"
Write-Warn $uMsg
# continue; copying may still succeed depending on ACLs/zone info
}
} else {
Write-Warn "Unblock-File cmdlet not available on this system; skipping unblock step."
}
} catch {
Write-Warn "Unexpected error during unblock step: $($_.Exception.Message)"
}
# === Main operations ===
try {
Write-Info "Script started. App='$AppName' Version='$Version' Source='$sourcePath'"
# Ensure destination directory exists
try {
if (-not (Test-Path -Path $destVersionPath)) {
New-Item -Path $destVersionPath -ItemType Directory -Force | Out-Null
Write-Info "Created destination directory: $destVersionPath"
} else {
Write-Info "Destination directory already exists: $destVersionPath"
}
} catch {
$msg = "Failed to create destination directory $destVersionPath : $($_.Exception.Message)"
Write-ErrorLog $msg
throw $msg
}
# Copy the file
try {
$destFile = Join-Path -Path $destVersionPath -ChildPath (Split-Path -Path $sourcePath -Leaf)
Copy-Item -Path $sourcePath -Destination $destFile -Force -ErrorAction Stop
Write-Info "Copied '$sourcePath' to '$destFile'"
} catch {
$msg = "Copy failed: $($_.Exception.Message)"
Write-ErrorLog $msg
throw $msg
}
# Clean up old version folders for this app older than $daysToKeep days
try {
if (Test-Path -Path $destRoot) {
$threshold = (Get-Date).AddDays(-1 * [int]$daysToKeep)
$currentFull = (Get-Item -LiteralPath $destVersionPath).FullName
$folders = Get-ChildItem -Path $destRoot -Directory -ErrorAction Stop |
Where-Object { $_.FullName -ne $currentFull } |
Where-Object {
$time = if ($_.CreationTime -ne $null) { $_.CreationTime } else { $_.LastWriteTime }
$time -lt $threshold
}
if ($folders.Count -gt 0) {
foreach ($f in $folders) {
try {
Remove-Item -Path $f.FullName -Recurse -Force -ErrorAction Stop
Write-Info "Removed old version folder: $($f.FullName)"
} catch {
Write-Warn "Failed to remove folder $($f.FullName) : $($_.Exception.Message)"
}
}
} else {
Write-Info "No old version folders older than $daysToKeep days found for $AppName"
}
} else {
Write-Info "No app root folder found at $destRoot; nothing to clean."
}
} catch {
Write-Warn "Cleanup step failed: $($_.Exception.Message)"
}
Write-Info "Script completed successfully."
exit 0
}
catch {
$err = $_.Exception.Message
Write-ErrorLog "Unhandled error: $err"
Write-ErrorLog "StackTrace: $($_.Exception.StackTrace)"
Write-Host "Unhandled error: $err" -ForegroundColor Red
exit 1
}
Notes and recommendations
- Unblock behavior: the script calls
Unblock-Fileif available and logs success or warning on failure; copying proceeds even if unblock fails (logged). - Run as Administrator to allow creation of the event source and writing to
C:\Windows. - If
fileAhas an extension, pass it via-SourceFileor change$defaultFileName. - To change retention, modify
$daysToKeep.