# moveFilesToZip.ps1
# Version 0.02
# This little function is to automate the process of
# a. Creating an empty zip file
# b. Gathering source files to move
# c. Adding those files into zip archive
# d. Purging the source files
# e. Keeping track of memory utilization so that program would not exceed available RAM and crash
# f. Ensure that accidential directories such as C:\Windows and C:\ProgramData are detected as inputs to cancel execution
# Notes:
# To reduce memory consumption, the workflow is sequential so that only one file is processed per iteration
# This comes at the cost of performance efficiency, although it must be so to overcome a host's memory limitations.
# Optimization has been done by keeping the zip file locked during operations to save io cycles of instantiating the zip update command per iteration.
# Credentials
$username='intranet\backupadmin'
$password='somecomplexpassword'
$encryptedPassword=ConvertTo-SecureString $password -AsPlainText -Force
$credentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $userName,$encryptedPassword;
$computerNames=@(
'server001',
'server002'
)
$parentDirectory='C:\Program Files\Path\To\Logs'
$compressFilesOlderThanDays=7
$destinationZipFile="B:\oldLogFiles.zip"
$maxZipSize='1GB'
$recurse=$true
function moveFilesToZip ($parentDirectory,$filesDaysOlderThan,$zipFile,$maxZipSize,$recurse=$true,$verbose=$true){
# Built-in safety check
$hardStopLocations=@(
'C:\Windows',
'C:\ProgramData',
'C:\Users'
)
$ErrorActionPreference='ignore'
if($hardStopLocations|?{$parentDirectory -like "$_*"}){
write-warning "$parentDirectory is not a safe location to compress files."
return $false
}
function displaySessionInfoInTitleBar{
$thisProcessName=.{
$psProcess = get-process -id $pid # Accessing the system reserved variable as pointer to this object's PID
$psInstances = (get-process -Name $psProcess.name).count # gathering all instances of this process name
if ($psInstances -gt 1){ # Distinguish this PS Session from other sessions, if any
return $("{0}#{1}" -f $psProcess.name,$($psInstances-1))
}else{
return $psProcess.name
}
}
$GLOBAL:psCpuPerf = new-object System.Diagnostics.PerformanceCounter("Process","% Processor Time",$thisProcessName) # Create the Performance Counter Object
$GLOBAL:timer = New-Object System.Timers.Timer # Create a timer with 1000ms interval
$timer.Interval = 1000
$null=Register-ObjectEvent -InputObject $timer -EventName Elapsed -Action {
$psInfo = Get-Process -id $pid
[int]$memory = $psInfo.workingset/1MB
[int]$cpu = $psCpuPerf.NextValue()/$env:NUMBER_OF_PROCESSORS
#[string]$currentDir=(get-location).ProviderPath
[string]$psVersion=$Host.Version
[string]$hostName=$Host.Name
$Host.UI.RawUI.WindowTitle="$hostName`: $ENV:Username | PSVersion: $psVersion | MemoryUtilization: $memory`MB | CpuUtilization: $cpu%"
#$Host.UI.RawUI.WindowTitle="SessionType: $hostName | User: $ENV:Username | PSVersion: $psVersion | CurrentDirectory: $currentDir | CpuUtilization: $cpu% | MemoryUtilization: $memory`MB"
}
$timer.start() # kickoff timer
}
function isMemoryAvailable($acceptableUtilization=0.75,$verbose=$false){
$memory=Get-CIMInstance Win32_OperatingSystem
$availableMemory=$memory.FreePhysicalMemory
$systemMemory=$memory.TotalVisibleMemorySize
if($verbose){
write-host "Memory check: $([math]::round($systemMemory/1048576,2)) GB detected, and $([math]::round($availableMemory/1048576,2)) GB is currently free."
}
if(($availableMemory/$systemMemory) -ge $acceptableUtilization){
return $false
}else{
return $true
}
}
# Start displaying session stats
displaySessionInfoInTitleBar
# Check RAM
isMemoryAvailable -verbose $true
#$powerShellMaxRamPerSession=Get-Item WSMan:\localhost\Shell\MaxMemoryPerShellMB
# Fastest way to list all files in a directory
if(($filesDaysOlderThan -eq 0) -and $recurse){
$getFiles = "cmd.exe /C dir '$parentDirectory' /S /B /W /A:-D"
$filesToCompress = Invoke-Expression -Command:$getFiles
}else{
$filesToCompress=(gci $parentDirectory|?{!$_.PSIsContainer -and ($_.LastWriteTime -lt (Get-Date).AddDays(-$filesDaysOlderThan))}).FullName
}
if(!(test-path $zipFile)){
#New-Item -Name $zipFile -ItemType File -Force # Error: cannot create file with this extension using command
$fileIo=[System.IO.File]::Create($zipFile) # invoke dotnet to overcome PShell filetype checking
$fileIo.Close()
}
function toRotateFilename($fileName,$maxGb){
$size=(Get-Item $fileName).length
if($size -gt $maxGb/1){
return $true
}else{
return $false
}
}
Add-Type -Assembly 'System.IO.Compression.FileSystem' #including .NET 3.x or 4.x assembly
$compressionLevel = [System.IO.Compression.CompressionLevel]::Optimal
$zipIo = [System.IO.Compression.ZipFile]::Open($zipFile, 'update')
foreach ($file in $filesToCompress){
#$validLocation=!($hardStopLocations|?{$file -like "$_*"}) # perform location safety check for each file i case symlinks were used
$toRotateFileName=toRotateFilename $zipFile $maxZipSize
if($toRotateFileName){
#$randomSuffix=-join ((65..90) + (97..122) | Get-Random -Count 5 | % {[char]$_})
$filenameArray=[regex]::match($(split-path $zipFile -Leaf),'(.*)\.(.*)').groups
$extractedFilename=$filenameArray[1].Value
$suffix=$filenameArray[2].Value
$timeStamp=get-date -Format yyyyMMdd-HHmm
$newFileName=$($extractedFilename+"_$timeStamp."+$suffix)
$newFilePath=join-path $(split-path $zipFile -parent) $newFileName
$zipIo.Dispose()
# rename-item $zipFile $newFileName -force
# the move-item method would consistently override duplicate file names
Move-Item -Path $zipFile -Destination $newFilePath -Force
new-item $zipFile
$zipIo = [System.IO.Compression.ZipFile]::Open($zipFile, 'update')
}
$validLocation=$true
if((isMemoryAvailable $systemMemory) -and $validLocation){
try{
$null=[System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($zipIo,$file,$(split-path $file -leaf),$compressionLevel)
$addedSuccessfully=$true
}catch{
Write-Error $_
$addedSuccessfully=$false
}
}elseif($validLocation){ # Releases memory
$zipIo.Dispose()
[GC]::Collect() # enforces garbage collection
$zipIo = [System.IO.Compression.ZipFile]::Open($zipFile, 'update')
try{
$null=[System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($zipIo,$file,$(split-path $file -leaf),$compressionLevel)
$addedSuccessfully=$true
}catch{
Write-Error $_
$addedSuccessfully=$false
}
}
if($addedSuccessfully -and $validLocation){
remove-item $file -Force
write-host "$file => moved to zip successfully." -ForegroundColor Green
}elseif($validLocation){
write-host "$file => NOT moved to zip successfully" -ForegroundColor Yellow
}
}
if($zipIo){$zipIo.Dispose()}
}
foreach($computer in $computernames){
$session=try{
New-PSSession -ComputerName $computer -Credential $credentials -EA Stop
}catch{
New-PSSession -ComputerName $computer -Credential $credentials -SessionOption $(new-pssessionoption -IncludePortInSPN)
}
if($session.State -eq 'Opened'){
write-host "Invoking functions on $computer`r`n----------------------------------------"
invoke-command -Session $session -ScriptBlock{
param ($moveFilesToZip,$parentDirectory,$compressFilesOlderThanDays,$destinationZipFile,$maxZipSize)
return [ScriptBlock]::Create($moveFilesToZip).invoke($parentDirectory,$compressFilesOlderThanDays,$destinationZipFile,$maxZipSize);
} -Args ${function:moveFilesToZip},$parentDirectory,$compressFilesOlderThanDays,$destinationZipFile,$maxZipSize
Remove-PSSession $session
}else{
write-warning "Unable to connect to $computer"
}
}
# moveFilesToZip.ps1
# Version 0.01
# This little function is to automate the process of
# a. Creating an empty zip file
# b. Gathering source files to move
# c. Adding those files into zip archive
# d. Purging the source files
# e. Keeping track of memory utilization so that program would not exceed available RAM and crash
# f. Ensure that accidential directories such as C:\Windows and C:\ProgramData are detected as inputs to cancel execution
# Notes:
# To reduce memory consumption, the workflow is sequential so that only one file is processed per iteration
# This comes at the cost of performance efficiency, although it must be so to overcome a host's memory limitations.
# Optimization has been done by keeping the zip file locked during operations to save io cycles of instantiating the zip update command per iteration.
$parentDirectory='C:\Program Files\Dynamics 365\Trace'
$compressFilesOlderThanDays=0
$destinationZipFile="\\Archive\crmLogs\$ENV:ComputerName\oldLogFiles.zip"
$recurse=$true
function moveFilesToZip ($parentDirectory,$filesDaysOlderThan,$zipFile,$recurse=$false,$verbose=$true){
# Built-in safety check
$hardStopLocations=@(
'C:\Windows',
'C:\ProgramData',
'C:\Users'
)
if($hardStopLocations|?{$parentDirectory -like "$_*"}){
write-warning "$parentDirectory is not a safe location to compress files."
return $false
}
function displaySessionInfoInTitleBar{
$thisProcessName=.{
$psProcess = get-process -id $pid # Accessing the system reserved variable as pointer to this object's PID
$psInstances = (get-process -Name $psProcess.name).count # gathering all instances of this process name
if ($psInstances -gt 1){ # Distinguish this PS Session from other sessions, if any
return $("{0}#{1}" -f $psProcess.name,$($psInstances-1))
}else{
return $psProcess.name
}
}
$GLOBAL:psCpuPerf = new-object System.Diagnostics.PerformanceCounter("Process","% Processor Time",$thisProcessName) # Create the Performance Counter Object
$GLOBAL:timer = New-Object System.Timers.Timer # Create a timer with 1000ms interval
$timer.Interval = 1000
$null=Register-ObjectEvent -InputObject $timer -EventName Elapsed -Action {
$psInfo = Get-Process -id $pid
[int]$memory = $psInfo.workingset/1MB
[int]$cpu = $psCpuPerf.NextValue()/$env:NUMBER_OF_PROCESSORS
#[string]$currentDir=(get-location).ProviderPath
[string]$psVersion=$Host.Version
[string]$hostName=$Host.Name
$Host.UI.RawUI.WindowTitle="$hostName`: $ENV:Username | PSVersion: $psVersion | MemoryUtilization: $memory`MB | CpuUtilization: $cpu%"
#$Host.UI.RawUI.WindowTitle="SessionType: $hostName | User: $ENV:Username | PSVersion: $psVersion | CurrentDirectory: $currentDir | CpuUtilization: $cpu% | MemoryUtilization: $memory`MB"
}
$timer.start() # kickoff timer
}
function isMemoryAvailable($acceptableUtilization=0.75,$verbose=$false){
$memory=Get-CIMInstance Win32_OperatingSystem
$availableMemory=$memory.FreePhysicalMemory
$systemMemory=$memory.TotalVisibleMemorySize
if($verbose){
write-host "Memory check: $([math]::round($systemMemory/1048576,2)) GB detected, and $([math]::round($availableMemory/1048576,2)) GB is currently free."
}
if(($availableMemory/$systemMemory) -ge $acceptableUtilization){
return $false
}else{
return $true
}
}
# Start displaying session stats
displaySessionInfoInTitleBar
# Check RAM
isMemoryAvailable -verbose $true
#$powerShellMaxRamPerSession=Get-Item WSMan:\localhost\Shell\MaxMemoryPerShellMB
# Fastest way to list all files in a directory
if(($filesDaysOlderThan -eq 0) -and $recurse){
$getFiles = "cmd.exe /C dir '$parentDirectory' /S /B /W /A:-D"
$filesToCompress = Invoke-Expression -Command:$getFiles
}else{
$filesToCompress=(gci $parentDirectory|?{!$_.PSIsContainer -and ($_.LastWriteTime -lt (Get-Date).AddDays(-$filesDaysOlderThan))}).FullName
}
if(!(test-path $zipFile)){
#New-Item -Name $zipFile -ItemType File -Force # Error: cannot create file with this extension using command
$fileIo=[System.IO.File]::Create($zipFile) # invoke dotnet to overcome PShell filetype checking
$fileIo.Close()
}
Add-Type -Assembly 'System.IO.Compression.FileSystem' #including .NET 3.x or 4.x assembly
$compressionLevel = [System.IO.Compression.CompressionLevel]::Optimal
$zipIo = [System.IO.Compression.ZipFile]::Open($zipFile, 'update')
foreach ($file in $filesToCompress){
#$validLocation=!($hardStopLocations|?{$file -like "$_*"}) # perform location safety check for each file i case symlinks were used
$validLocation=$true
if((isMemoryAvailable $systemMemory) -and $validLocation){
try{
$null=[System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($zipIo,$file,$(split-path $file -leaf),$compressionLevel)
$addedSuccessfully=$true
}catch{
Write-Error $_
$addedSuccessfully=$false
}
}elseif($validLocation){ # Releases memory
$zipIo.Dispose()
[GC]::Collect() # enforces garbage collection
$zipIo = [System.IO.Compression.ZipFile]::Open($zipFile, 'update')
try{
$null=[System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($zipIo,$file,$(split-path $file -leaf),$compressionLevel)
$addedSuccessfully=$true
}catch{
Write-Error $_
$addedSuccessfully=$false
}
}
if($addedSuccessfully -and $validLocation){
remove-item $file -Force
write-host "$file => moved to zip successfully." -ForegroundColor Green
}elseif($validLocation){
write-host "$file => NOT moved to zip successfully" -ForegroundColor Yellow
}
}
if($zipIo){$zipIo.Dispose()}
}
moveFilesToZip $parentDirectory $compressFilesOlderThanDays $destinationZipFile $recurse
# Deprecated version
# moveFilesToZip.ps1
# This little function is to automate the process of
# a. creating an empty zip file
# b. gathering source files to move
# c. adding those files into zip archive
# d.purge the source files
# To reduce memory consumption, the workflow is sequential so that only one file is processed per iteration
# This comes at the cost of performance efficiency. Optimization has been done by keeping the zip file locked
# during operations to save io cycles of instantiating the zip update command per iteration.
$parentDirectory='C:\temp'
$compressFilesOlderThanDays=1
$destinationZipFile='c:\temp\oldFiles.zip'
function moveFilesToZip($parentDirectory,$filesDaysOlderThan,$zipFile,$allFiles=$false,$recurse=$false,$verbose=$true){
# Fastest way to list all files in a directory
if($allFiles -and $recurse){
$getFiles = "cmd.exe /C dir '$parentDirectory' /S /B /W /A:-D"
$filesToCompress = Invoke-Expression -Command:$getFiles
}else{
$filesToCompress=(gci $parentDirectory|?{!$_.PSIsContainer -and ($_.LastWriteTime -lt (Get-Date).AddDays(-$filesDaysOlderThan))}).FullName
}
if(!(test-path $zipFile)){
#New-Item -Name $zipFile -ItemType File -Force # cannot create file with this extension using command
$fileIo=[System.IO.File]::Create($zipFile) # invoke dotnet to overcome PShell filetype checking
$fileIo.Close()
}
Add-Type -Assembly 'System.IO.Compression.FileSystem' #including .NET 4.5 assembly
$compressionLevel = [System.IO.Compression.CompressionLevel]::Optimal
$zipIo = [System.IO.Compression.ZipFile]::Open($zipFile, 'update')
foreach ($file in $filesToCompress){
#$fileFullPath=$file.FullName
#$fileName=$file.Name
write-host "Moving $file into zip..."
try{
# Compress-Archive -Path $filePath -Update -DestinationPath $zipFile -ea Stop # requires PowerShell 5.0
#if ($file -notin $zipIo.Entries ){
$null=[System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($zipIo,$file,$(split-path $file -leaf),$compressionLevel)
remove-item $file -Force
#}else{
# write-warning "$file already exists within zip file"
# }
}catch{
Write-Warning $Error[0].exception.message
}
}
$zipIo.Dispose()
}
moveFilesToZip $parentDirectory $compressFilesOlderThanDays $destinationZipFile
Categories: