Capturing Non-Standard Powershell CmdLet Output for Flow Control - version-control

Currently trying to build a script utilizing cmdlets from the MS released "Team Foundation Server Power Tools" package.
I'm attempting to flow command logic from the success or failure of the "Update-TfsWorkspace" cmdlet however I can't seem get a return code out of the call nor can I capture the output using Out-String. I'm using Powershell v1.
update-tfsworkspace "C:\doesnotexist\" -recurse -version T
Yields a message of "Unable to determine the workspace." which is the error I'm trying to catch.
$ret = update-tfsworkspace "C:\doesnotexist\" -recurse -version T
Is expected to give me a $true/$false indicating success/fail but doesn't work.
update-tfsworkspace "C:\doesnotexist\" -recurse -version T | Out-Null
Is expected to prevent the cmdlet from writing the message but doesn't work.
trap{echo "fail"}
update-tfsworkspace $workspace_path -recurse -version T
Is expected to catch an error and write "fail" but doesn't work.
$msg = update-tfsworkspace $workspace_path -recurse -version T | Out-String
Is expected to populate a $msg variable with the host output but doesn't work.
I'm totally out of ideas here. Help would be appreciated!

Little hacky, but since I don't have TFS to try to figure something else out, see if this helps.

I would say that this cmdlet wasn't written correctly. First, since it didn't succeed it should have emitted an error object which would have caused $? to return false which you could have checked or trapped. Second, you can't suppress the error message using -ea 0. It looks like this snapin is using the Host api to write an error string to the host console. That's a spew!! For now, you could do what EBGreen suggests:
$msg = powershell.exe -nologo update-tfsworkspace "C:\doesnotexist\" -recurse -version T 2>&1
Just watch out for all the text your profile script spits out when a new instance of PowerShell starts up.

Your problem here is that the cmdlet is writing an error (Non-Terminating Error), but not throwing an exception (Terminating Error). You can make it throw an exception by adding the ErrorAction parameter:
trap{echo "fail"}
update-tfsworkspace $workspace_path -recurse -version T -ErrorAction "Stop"
This will cause the cmdlet to make all errors terminating (throwing an exception if it writes to the error stream).

Related

How do I check if a specific exception type has been thrown in Pester 5.4.0?

I want to write a test in PowerShell with Pester 5.4.0 to check whether a specific exception type has been thrown inside a script block. It should be fairly easy, but I am somehow not doing it right. Following along the Pester v5 documentation for the Should keyword and this pre-release Pester blogpost about using Should - Throw I wrote the following (very basic) script containing one test:
Import-Module Pester -RequiredVersion 5.4.0
Describe "Testing if a service is registed." {
It "This service should not be registered in the windows services app." {
{ Get-Service -Name "this service does not exist" } | Should -Throw -ExceptionType ([Microsoft.PowerShell.Commands.ServiceCommandException])
}
}
I would expect the test to catch the ServiceCommandException that is thrown when the Get-Service Cmdlet looks for a service that does not exist, and the test to pass. Instead the error is still thrown, Pester tells me that Expected an exception, with type [Microsoft.PowerShell.Commands.ServiceCommandException] to be thrown, but no exception was thrown., and the test fails:
I am probably doing something that's very obvious wrong here, but I looked at it for a while and did some research, and I cannot see what is wrong with my script.
the type of error you get from get-service is not a throwing/operation halting error. This means that while there is an error, Powershell will continue to process your script until completed.
to make sure that any command actually halts operation and triggers pesters should -throw, you can add -erroraction stop or the alias -ea stop to the get-service command:
The issue here, however you want to look at it, is that you are testing if a command is throwing an error, and handling it this way. however the command does not throw anything. it returns an error. its not a operation stopping thing that happens (even though you can force it by setting -erroraction).
what you might want to do instead is this:
It "Testing other method" {
Get-Service -Name "this service does not exist" -ErrorVariable er -ErrorAction SilentlyContinue
$er|should -not -HaveCount 0
$er.Exception|Should -BeOfType ([Microsoft.PowerShell.Commands.ServiceCommandException])
}

Powershell: cannot hide 'Access is denied' error on 'Remove-Item'

I would like to keep a Remove-Item instruction quiet, exception or not. I'm running below command in a script to delete a certificate:
Remove-Item $store\$thumbprint
If I run the script as local Admin, fine... it keeps quiet and the file is deleted.
If however I run it as unpriviledged user, I get an 'Access in denied' error as expected, but I would like to keep this quiet in any case.
I've tried the following:
$output = (Remove-Item $store\$thumbprint)
# or...
try{Remove-Item $store\$thumbprint} catch{}
# or...
Remove-Item $store\$thumbprint -ErrorAction SilentlyContinue
But I always get the error/exception
displayed on the console.
By default, a non-terminating error is generated by Remove-Item and it adds an error to the $Error variable without throwing an exception. To see what Windows PowerShell will do when a non-terminating error arises, look at the value of the $ErrorActionPreference variable (its default value is Continue).
The Access to the path '…' is denied is an example of such a non-terminating error so you can use ErrorAction parameter which overrides the value of the $ErrorActionPreference variable for the current command:
Remove-Item $store\$thumbprint -ErrorAction SilentlyContinue
On the other side, $ErrorActionPreference and the ErrorAction parameter don't affect how PowerShell responds to terminating errors that stop cmdlet processing. So if we are not sure whether an error is terminating or not then it's safe to handle any error the Try-Catch-Finally blocks using -ErrorAction Stop as follows:
try {
Remove-Item $store\$thumbprint -ErrorAction Stop
} catch {
### A Catch block can include commands for tracking the error
### or for recovering the expected flow of the script
}

Powershell - Skip files that cannot be accessed

I'm trying to recursively delete all files and folders inside of a folder with Powershell via:
Remove-Item "C:\Users\user\Desktop\The_folder\*" -Recurse -Force
My problem is that whenever I run it, I get:
Cannot remove item C:\Users\user\Desktop\The_folder\Delete: The process cannot access the file 'C:\Users\user\Desktop\The_folder\Delete' because it is being used by another process.
How do I skip any files I don't have access to because they are in use (i.e. the same way a user would via GUI asking if wanting to skip all files that cannot be accessed)?
I tried the following, but received error:
Remove-Item "C:\Users\mstjean\Desktop\The_folder\*" -Recurse -Force -ErrorAction Continue
Remove-Item : Cannot remove item C:\Users\mstjean\Desktop\The_folder\Delete:
The process cannot access the file 'C:\Users\mstjean\Desktop\The_folder\Delete'
because it is being used by another process.
At line:1 char:1
+ Remove-Item "C:\Users\mstjean\Desktop\The_folder\*" -Recurse -Force -ErrorAction ...
+ CategoryInfo: WriteError: (C:\Users\mstjea...e_folder\Delete:DirectoryInfo) [Remove-Item], IOException
+ FullyQualifiedErrorId: RemoveFileSystemItemIOError,Microsoft.PowerShell.Commands.RemoveItemCommand
If you want to suppress the error message and continue executing, you need to use -ErrorAction Ignore or -ErrorAction SilentlyContinue.
See Get-Help about_CommonParameters:
-ErrorAction[:{Continue | Ignore | Inquire | SilentlyContinue | Stop |
Suspend }]
Alias: ea
Determines how the cmdlet responds to a non-terminating error
from the command. This parameter works only when the command generates
a non-terminating error, such as those from the Write-Error cmdlet.
The ErrorAction parameter overrides the value of the
$ErrorActionPreference variable for the current command.
Because the default value of the $ErrorActionPreference variable
is Continue, error messages are displayed and execution continues
unless you use the ErrorAction parameter.
The ErrorAction parameter has no effect on terminating errors (such as
missing data, parameters that are not valid, or insufficient
permissions) that prevent a command from completing successfully.
Valid values:
Continue. Displays the error message and continues executing
the command. "Continue" is the default value.
Ignore. Suppresses the error message and continues
executing the command. Unlike SilentlyContinue, Ignore
does not add the error message to the $Error automatic
variable. The Ignore value is introduced in Windows
PowerShell 3.0.
Inquire. Displays the error message and prompts you for
confirmation before continuing execution. This value is rarely
used.
SilentlyContinue. Suppresses the error message and continues
executing the command.
Stop. Displays the error message and stops executing the
command.
Suspend. This value is only available in Windows PowerShell workflows.
When a workflow runs into terminating error, this action preference
automatically suspends the job to allow for further investigation. After
investigation, the workflow can be resumed.
If you're having terminating errors that -ErrorAction is not trapping, then you have to trap them yourself with a try / catch.
Here's a naive example:
Get-ChildItem "C:\Users\user\Desktop\The_folder\*" -Recurse -Force `
| Sort-Object -Property FullName -Descending `
| ForEach-Object {
try {
Remove-Item -Path $_.FullName -Force -ErrorAction Stop;
}
catch { }
}
Here, I'm using -ErrorAction Stop to turn all non-terminating errors into terminating errors. Try will trap any terminating error. The catch block is empty, however, so you're trapping everything and then not doing any error handling. The script will continue silently. This is basically equivalent to VBScript's On Error Resume Next. You have to iterate through the files, however, otherwise Remove-Item will stop at the first error.
Note that I have a Sort-Object in there. That's so the items coming through the pipeline are in reverse order. That way, files and subdirectories will be deleted before the directories that contain them. I'm not 100% sure if that method is perfect, but I think it should work. The alternative is really messy.
Obviously, there's no way to tell from output when an error occurs or what wasn't deleted. We're trapping all errors and then throwing them away. Usually an empty catch block is a really bad idea, so use this method with caution!
You'll note that I'm not actually testing to see if the file is opened or locked. That's because it's kind of a waste of time. We don't really care why the file can't be deleted, just that it can't and when it can't we skip it. It's easier (and faster) to try to delete the file and trap the failure than it is to check if it's locked, use a conditional to decide to delete the file, and then delete the file or continue.

How to get the error code when there is error in powershell?

My snippet is something like this:
$msg=Remove-Item -Recurse -Force C:\users\bkp 2>&1
if ($LASTEXITCODE -eq 1)
{
"Encountered error during Deleting the Folder. Error Message is $msg. Please check." >> $LogFile
exit
}
The folder C:\users\bkp does not exist. Even though $msg gives me the error message $LASTEXITCODE is still 0. How do I capture as a flag?
You can use the $? automatic variable to determine the result of the last command. If you need access to the actual error, you can use the $Error automatic variable. The first item in the array is the last error thrown:
Remove-Item -Recurse -Force C:\users\bkp 2>&1
if( -not $? )
{
$msg = $Error[0].Exception.Message
"Encountered error during Deleting the Folder. Error Message is $msg. Please check." >> $LogFile
exit
}
$LASTEXITCODE is strictly for command line programs to return their status. Cmdlets that are built into PS, such as Remove-item return their errors in up to 3 ways. For warnings, they write messages (or other .NET objects) to the "warning stream". In PSv3 there is a straightforward way to redirect that stream to a file: cmdlet blah blah blah 3>warning.out. The second is via the error stream. That stream can be redirected as well ... 2>error.out, or more typically errors are caught with try/catch or trap, or written to a variable with the -ErrorVariable parameter (see help about_commonparameters). The third way is for errors to be "thrown". Unless caught (try/catch or trap), a thrown error will cause the script to terminate. Thrown errors generally are subclasses of the .NET class system.Management.Automation.ErrorRecord. An ErrorRecord provides a lot more information about an error than a return code.
If remove-item fails due to a file not found error, it writes a System.Management.Automation.ItemNotFoundException to the error stream. Using a try/catch you can filter for that specific error or other specific errors from remove-item. If you are just typing in PS commands from the command line you can enter $error[0]|select-object * to get a lot of info on the last error.
You could do this:
try {
Remove-Item -Recurse -Force C:\users\bkp 2>&1
} catch {
# oops remove-item failed. Write warning then quit
# replace the following with what you want to do
write-warning "Remove-item encounter error: $_"
return # script failed
}

Powershell 2: Unable to suppress warning message

I am making a cmdlet call to 'set-distributiongroup' in powershell 2. I am simply setting the value of the parameter 'hiddenFromAddressListsEnabled' to a pre-defined boolean value.
However, no matter what I try, it displays a warning message if the boolean assignment is not actually changing the current value of 'hiddenFromAddressListsEnabled'.
Here is the main command I'm invoking:
set-DistributionGroup -identity TestGroup `
-hiddenFromAddressListsEnabled=$true
Let's semantically define what I have above as 'command'.
Now, I've tried adding several different variants, all with proper line-continuation and syntax. Here are those variants:
command > $null
command 2> $null
command -ErrorAction:silentlycontinue
command -ErrorVariable $throwAway
command -WarningAction:silentlycontinue
command -WarningVariable $throwAway
$var = command
Regardless of various combinations of one or more of the above, I still get a yellow WARNING: message spit to output. Specifically, this:
WARNING: The command completed successfully but no settings of
'<xxxxxx>/Users/TestGroup' have been modified.
Any suggestions on a key concept I'm not understanding? I want the command to not produce this output, and I want it to silently continue if this occurs.
Thanks!!
I've been trying to suppress the warning messages when stopping a service:
WARNING: Waiting for service 'Service Description' to finish stopping...
The following worked for me:
Stop-Service $svc.Name -WarningAction SilentlyContinue
If it's just a warning that cause problem why don't you set in your script $WarningPreference variable ?
PS C:\> $WarningPreference='silentlycontinue'
PS C:\> Write-Warning "coucou"
PS C:\> $WarningPreference='continue'
PS C:\> Write-Warning "coucou"
AVERTISSEMENT : coucou
just bumped this topic when searching on the issue,
my case PowerShell v2 , only after setting
$WarningPreference = "SilentlyContinue"
Write-Warning "blah" - returned me nothing... the parameter on the command didn't changed much too on my end.
You may be hitting this bug: http://connect.microsoft.com/PowerShell/feedback/details/541500/warning-verbose-and-debug-streams-do-not-respect-action-preferences-the-way-they-should
Anyway, your command should look like:
Set-DistributionGroup -Identity TestGroup -HiddenFromAddressListsEnabled $true
Your command is wrong. Which is the the reason why you get a yellow error message. The command should look like:
Set-DistributionGroup -Identity TestGroup -HiddenFromAddressListsEnabled $true
Or
Set-Distributionaliste -Identity TestGroup -HiddenFromAddressListsEnabled:$true
But not
Set-DistributionGroup -Identity TestGroup -HiddenFromAddressListsEnabled=$true
I was getting the same problem with the Exchange Management Console in 2010. Problem is the EMC runs on PowerShell 2.0, which as stated before has some bugs around warning preferences.
I found a cheeky workaround was to run my script in a vanilla PowerShell 4.0 shell, and then import the EMC cmdlets and start a new remote PS session like so...
. 'C:\Program Files\Microsoft\Exchange Server\V14\bin\RemoteExchange.ps1'
Connect-ExchangeServer -auto
...then, -WarningAction:SilentlyContinue suddenly starts behaving itself.
You should try
Set-DistributionGroup -Identity TestGroup -HiddenFromAddressListsEnabled $true -WarningAction silentlyContinue
I bumped into same issue, the following command seems to do the job (PS 3.0) :
Stop-Service<or whatever command> $svc.Name -WarningPreference SilentlyContinue
Don't know exactly what difference it makes with -WarningAction, though.
Hope this helps !
If you invoke powershell versión 2.0, you should use "-WarningAction silentlyContinue". I was having the same issue, but in the script, if I invoke, for example "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -versión 2.0" , then you can use this parameter. I was trying on a scheduled task and using a ps1 script.
Try something like that:
PS C:\> {command} | Out-Null
For more Information: Technet: Out-Null