Powershell script Compress-Archive partial failure - powershell

I have a script that includes:
try {
Compress-Archive -Path "$($folder.FullName)\*" -CompressionLevel Optimal -DestinationPath $FullPath -Force
} catch {
Write-Output "`nFailed to create zip"
}
Some of the files in the -Path folder are opened by another user, so Compress-Archive is unable to add them to the zip. It reports this failure to stderr, however it doesn't throw an error.
Is there any way I can determine that the command has only partially succeeded without parsing stderr or re-opening the zip and comparing contents?

It sounds like Compress-Archive is throwing a non-terminating error, which you cannot catch with try / catch.
However, if you add -ErrorAction Stop to the Compress-Archive call, the non-terminating error will be promoted to a script-terminating error, which will trigger your catch handler.
For an overview of PowerShell's complex error-handling rules, see this GitHub issue.

Related

Remove-Item -Force on NULL Filewatcher file removed 1000 files on my server, is this a Powershell bug? Or just my bad code?

So, I am developing a script using FileSystemWatcher similar to this one: https://powershell.one/tricks/filesystem/filesystemwatcher
I only use the Created event.
I then run the following code on the files that are "Created."
I met a really unexpected error when I ran this code on a file that was already removed by another piece of code. So basically, the "Remove-WrongFileType" function received a file that was NULL, just nothing. And then it just started deleting tons of different files on my server.
I run my script from C:\ and I obviously gave it to high rights. However, I find it really strange that when the $Path is Null, the script just finds files to remove. I've managed to fix this in my code, by checking first if the path to the file leads to something, however I want to learn what caused the script to crash this hard, and why the Get-ChildItem finds files when the $Path is a NULL file. I wonder if this could be some kind of bug in Powershell? (Most likely not.. but I wonder..)
Function Remove-WrongFileType {
Param (
[string]$Path
)
$Files = Get-ChildItem -Path $Path -Force -Recurse
foreach($file in $Files) {
if(-not (Assert-LegalFileType -File $file.FullName){
Remove-Item -Path $file.Fullname -Force
Add-ToLog -logString “File $file was removed because of illegal filetype”
}
}
}
Function Assert-LegalFileType {
Param (
[string]$File
)
if(Test-Path -Path $File -PathType Container){
return $true
}
$fileToCheck = Get-Item -Path $File
$ExtensionOfFile = $fileToCheck.Extension
foreach($type in $AllowedFiles){
if($ExtensionOfFile -match $type) {
return $true
}
}
}
So I looked up what happens when you pass NULL to Get-childitem. And it is a known issue apparently. Get-ChildItem -Path $null does not throw an error #8708
A comment describe the same issue:
One of the side effects of this bug/feature could be to accidentally delete your system when you are using the output of this command piped to say a $_.Delete(). That is exactly what happened when I refactored my code to delete previous test runs; so
From :
Get-ChildItem -Path C:\SourceCodeTLM\testRunResults-Include * -File -Recurse | foreach { $.Delete() }
To:
$testRunResults= "C:\SourceCodeTLM\testRunResults"
Get-ChildItem -Path $testRunResults-Include * -File -Recurse | foreach { $.Delete() }
and forgot to initialize the variable while doing a debug.
In the worst case, I expected an error but instead, the cmd ran and started deleting my current dir content (Which by default was PS C:\windows\system32>).
Before I could understand what happened and pressed ctrl+c; enough files were deleted to corrupt my system. I had to restore and all of my stuff on my machine was lost. I learned this lesson the hard way but maybe others don't have to :). May be giving an error (when null) or keeping this parameter (mandatory) would be better from a risk standpoint :).
So yeah, don’t pass null to get-childitem and try to force delete the output with high privileges.

Why is my try catch error passing as successful even with -ErrorAction Stop on?

Below is a simple Try Catch but when I was testing it, when successful it appears to file so I included a location which didnt exist into the variable to test the failure, but oddly even though the failure appears in the ISE window the out still shows as suceeded maing something isnt quite right with below, any ideas as Im lost at what is wrong.
## The location/filename where the Logs will be stored
$varfullpath = "I:\Dev\BI\Reference Data\Release_Event_log.txt"
## The location/filename of the Source to copy from
$sourceDirectory = "C:\Users\Simon.Evans\Documents\Source Data\kljhkjlOS\"
## The location/filename of the Destination to copy to
$destinationDirectory = "I:\Dev\BI\Projects\Powershell\Test Area\Source Data\OkjhkhDS\"
$Server = "CHH-BITEST"
$CurrentDate = Get-Date
try{
Get-ChildItem -Path $sourceDirectory -File -Filter "*.csv" | Copy-Item -Destination $destinationDirectory -ErrorAction Stop
Write-Log -Message "Copy from $sourceDirectory to $destinationDirectory suceeded" -path $varfullpath
}
catch{
$Error[0] | Write-Log -path $varfullpath
Write-log -Message "Copy from $sourceDirectory to $destinationDirectory Failed" -Level Error -path $varfullpath
}
Start-Process notepad $varfullpath ## Opens the file immediately for review
The error is thrown by the get-childitemcommand.
Add the -ErrorAction Stop to this command like that:
Get-ChildItem -Path $sourceDirectory -File -Filter "*.csv" -ErrorAction Stop | Copy-Item -Destination $destinationDirectory -ErrorAction Stop
Some additions to #guiwhatsthat's answer:
PowerShell distinguishes between terminate errors and non-terminating errors. With the help of the common ErrorAction parameter you can setup the behaviour of a cmdlet if such kind of error occurrs. If you want to change the behaviour of multiple cmdlets, or sections in your script you can change the value of $ErrorActionPreference.
From PowerShell documentation:
$ErrorActionPreference
Determines how PowerShell responds to a non-terminating error (an error that does not stop the cmdlet processing) at the command line or in a script, cmdlet, or provider, such as the errors generated by the Write-Error cmdlet.
You can also use the ErrorAction common parameter of a cmdlet to override the preference for a specific command.
Valid values:
Stop: Displays the error message and stops executing.
Inquire: Displays the error message and asks you whether you want to continue.
Continue: Displays the error message and continues (Default) executing.
Suspend: Automatically suspends a workflow job to allow for further investigation. After investigation, the workflow can be resumed.
SilentlyContinue: No effect. The error message is not displayed and execution continues without interruption.

try/catch error handling

I'm trying to create a script to delete all files out of %temp%. It works but now I need to get rid of the errors. Below is what I have but I'm still getting errors. Any ideas?
try
{
Get-Childitem $Env:temp | Remove-Item -Recurse -Force
}
Catch
{
}
Lots of files in temp can be in use or otherwise inaccessible for removable as that is the scratch space for processes to leave data.
Most common cmdlets support -ErrorAction so that you don't have to the change the default one and risk missing an important error. In your case you can use ...
Get-Childitem $Env:temp | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
Since you are not generating terminating errors which is what Try is for you can just remove the block entirely.
FWIW if you really do need try/catch make sure you use terminating errors
try{
Get-ChildItem $Env:temp | Remove-Item -Recurse -Force -ErrorAction Stop
} Catch{
# Handle Stuff
Write-Host "Removal not 100% successful"
}

Powershell Logging Errors

I have a simple script to delete a file, I know it could be more robust, but here it is
$LogFile = ".\deleteRegPol.log"
try
{
Remove-Item "c:\test\new text document.txt" -force
Add-Content $LogFile -Value "We ran the command"
}
catch [Exception]
{
Add-Content $LogFile -Value $_
}
finally
{
}
When the file I am trying to delete doesn't exist, I get an error on the command line but in my log file, it says the command ran. This is telling me that an exception was not thrown resulting in the flow going to the catch block. Why not?
PowerShell does not normally throw an exception when there is an error. Instead it writes a record to the error stream, which you can redirect. To force it to throw there are two options. One is to set the global error preference to stop:
$ErrorActionPreference = "Stop"
The other is to set the ErrorAction parameter to stop. This is supported for cmdlets that accept the so-called common parameters, which Remove-Item does:
Remove-Item "c:\test\new text document.txt" -force -EA "Stop"
To redirect the error stream you use the code 2:
Remove-Item "c:\test\new text document.txt" -Force 2>> $LogFile
That would append the error to the log file. The record isn't written to the error stream if your option is "Stop" however. It is simply included in the exception that is thrown.
Add the ErrorAction switch to your Remove-Item command:
Remove-Item "c:\test\new text document.txt" -force -ErrorAction Stop
There's quite a good treatment on error handling here:
http://blogs.msdn.com/b/kebab/archive/2013/06/09/an-introduction-to-error-handling-in-powershell.aspx

UnauthorizedAccessException using Copy-Item on remote fileserver

I'm trying to copy about 10 folders each containing a ~3KB .txt file onto a remote fileshare with some seconds latency. I'm using Powershells Copy-Item like this:
try
{
Copy-Item -Path $source -Destination $destination -Recurse -ErrorAction Stop
}
catch
{
Write-Error $_.Exception.ToString()
}
The user running the script has read, write and execute permissions on the fileserver share and on the local source.
On first run, the destination folder is empty. Everything works fine.
On second run, the files and folders already exist. So before running the code above I first run a check using Test-Path and in case the folder exists a delete using Remove-Item like this:
try
{
if(Test-Path -Path $path -ErrorAction Stop)
{
Remove-Item -Recurse -Path $path -ErrorAction Stop
}
}
catch
{
Write-Error $_.Exception.ToString()
}
Nobody else edits those files. However, when running the script a dozent times, once in a while, for a reason I don't understand, i'm suddenly getting UnauthorizedAccessException errors for some of the folders while copying. The exact error is:
System.UnauthorizedAccessException: access denied
---> System.ComponentModel.Win32Exception: access denied
in Microsoft.PowerShell.Commands.FileSystemProvider.NativeDirectoryExists(String
path) in
System.Management.Automation.SessionStateInternal.IsItemContainer(CmdletProvider
providerInstance, String path, CmdletProviderContext context
please note: I'm getting those errors AFTER the deletion of the old files on the remote fileserver has compleated successfully.
This is a years old post but maybe one can benefit from that. You don't have to remove beforehand. You can just use -Force to override existing files.
try
{
Copy-Item -Path $source -Destination $destination -Recurse -ErrorAction Stop -Force
}
catch
{
Write-Error $_.Exception.ToString()
}
You may need try to run the command line as Administrator.
Masi, Powershell copy-item function is not really that great IMO. Why not use robocopy/Powershell hybrid here?
Example:
$source = "C:\temp"
$destination ="\\\RemoteServer\Temp"
robocopy $source $destination /s /mt:8