Powershell: IF, TRY and CATCH block in powershell script - powershell

I am using try and catch block inside IF condition, but I am not getting the desired results.
If (Test-Path -Path "$env:DigiCertificate"){
try {
signtool sign /f $env:DigiCertificate /v /t http://timestamp.sectigo.com /a /fd SHA256 /p $env:DigicertsPassword $MediaFolderPath\scripts\*.ps1 $MediaFolderPath\tools\*.exe
}catch
{
Write-Host "required certificate not found to sign" -ForegroundColor Yellow
Write-Host "$($_.Exception.Message)." -ForegroundColor Red
exit 1
}
}
In the above code I am expecting the IF condition should check for the -Path and it should fail if it does not find the DigiCertificate. Apart from try and catch block even IF condition should work, but I am trying to do it but I am missing something.

Related

Powershell script to remotely change registry setting based on version or installation path of program

I have a Powershell script that remotely adds a registry setting to computers on the network depending on which version of MS Office they have installed. At the moment, the script looks in c:\Program Files\Microsoft Office, then checks to see there is either an Office15 or Office16 folder than applies the appropriate reg entry. The problem is that I've discovered that some assets on the network also have the 32 bit version of MS Office, so their installations are going to be in c:\Program Files (x86)\Microsoft Office (\Office15 or \Office16), even though the reg entries added will be the same.
At the moment, I can't figure out how to make the script look for the two possible installation locations it could be in first (Program Files (x86)\Microsoft Office or Program Files\Microsoft Office), before determining what to do next (If ($Ver.name -Contains 'Office16')) etc.
The script:
$Asset = Read-Host "Please enter the asset number of the machine"
$User = Read-Host "Please enter the username of the users account"
$SID = Get-ADUser -Identity $User
If (Test-Connection $Asset -ErrorAction SilentlyContinue){
$Ver = Get-ChildItem \\$Asset\c$\Program Files\Microsoft Office
If ($Ver.name -Contains 'Office16') {
Write-Host ""
$RegPath = \\$($Asset)\HKEY_USERS\$($SID.sid)\Software\Microsoft\Office\16.0\Outlook\Preferences
REG ADD $RegPath /f /v DelegateSentItemsStyle /t REG_DWORD /d 1
} ElseIf ($Ver.name -Contains 'Office15') {
Write-Host ""
$RegPath = \\$($Asset)\HKEY_USERS\$($SID.sid)\Software\Microsoft\Office\15.0\Outlook\Preferences
REG ADD $RegPath /f /v DelegateSentItemsStyle /t REG_DWORD /d 1
} Else {
Write-Host ""
Write-Host "Unable to determine the version of Office installed." -ForegroundColor Red
}
} Else {
Write-Host ""
Write-Host "Unable to contact the machine. Please ensure you have enter the correct asset number" -ForegroundColor Green
}
pause
Any ideas appreciated.
Cheers.
Get-ChildItem can look into an array of "paths", hence you can store both known paths for Microsoft Office in an array and search for both of them. I've changed the order of the condition statements a bit, I personally find this more straight forward:
$Asset = Read-Host "Please enter the asset number of the machine"
$User = Read-Host "Please enter the username of the users account"
$SID = Get-ADUser -Identity $User
$paths = #(
"\\$Asset\c$\Program Files (x86)\Microsoft Office"
"\\$Asset\c$\Program Files\Microsoft Office"
)
If (-not (Test-Connection $Asset -Quiet)) {
Write-Warning "Unable to contact the machine. Please ensure you have enter the correct asset number"
break
}
$Ver = Get-ChildItem $paths -ErrorAction SilentlyContinue -ErrorVariable Errors
if(-not $Ver) {
Write-Warning "Failed to retrieve paths on $Asset"
$Errors.Exception.Message
break
}
if($Ver.Name -Contains 'Office16') {
$RegPath = "\\$Asset\HKEY_USERS\$($SID.sid)\Software\Microsoft\Office\16.0\Outlook\Preferences"
REG ADD $RegPath /f /v DelegateSentItemsStyle /t REG_DWORD /d 1
}
elseif($Ver.Name -Contains 'Office15') {
$RegPath = "\\$Asset\HKEY_USERS\$($SID.sid)\Software\Microsoft\Office\15.0\Outlook\Preferences"
REG ADD $RegPath /f /v DelegateSentItemsStyle /t REG_DWORD /d 1
}
else {
Write-Warning "Unable to determine the version of Office installed."
}

Exception handling in powershell when it's invoked from batch script

Unfortunately, my host application can't directly execute PowerShell scripts, so I have written a batch script which calls PowerShell script with content
#echo off
echo calling upgrade product with argument %2
if [%1] == [] (
powershell -ExecutionPolicy UnRestricted -command "%~dp0Product1.ps1 "ProductConfig.xml" -verbose; exit $LASTEXITCODE"
) else (
cd %1
powershell -ExecutionPolicy UnRestricted -command "%1\UpgradeProduct.ps1 %2 -verbose; exit $LASTEXITCODE"
)
And in my powershell script i have code like
$ErrorActionPreference="Stop"
try{
Copy-Item -Path $source -Destination $dest
}catch{
Write-Warning "Some Error"
}
This executes fine when i execute the script from the PowerShell window(if $source is not found it will throw a terminating error and prints Some Error). But when executed from batch script if $source is not found Copy-Item throws a non terminating error and continues(Dosen't print Some Error).
How can i make the Copy-Item to throw a terminating error if $Source is not found?
You have nothing that stop in your catch block.
I presume Your Write-Warning is triggered, but code after your block is runned.
You must return something in your catch block, like:
$ErrorActionPreference="Stop"
try{
Copy-Item -Path $source -Destination $dest
}catch{
Write-Warning "Some Error"
#$LASTEXITCODE is a special variable in powershell
$LASTEXITCODE = 1
exit $LASTEXITCODE
}
Note $LASTEXITCODE variable, it is a special variable, the equivalent to %errorlevel%, that is used by commands like "cmd calls" by PowerShell.
You can test it by using this command in PowerShell:
cmd.exe /c exit 5
$LASTEXITCODE
I suggest you to first do a check if path exists:
try{
if(Test-Path $source){
Copy-Item -Path $source -Destination $dest -ErrorAction Stop
}else{
Write-Warning "$source not found."
$LASTEXITCODE = 1
exit $LASTEXITCODE
}
}catch{
Write-Error "Exception during copy: $($_)"
$LASTEXITCODE = 1
exit $LASTEXITCODE
}
Point of interest:
ErrorAction is used at Function level. Set it at script level will include this settings for many commands.
Exception will be thrown only if copy fail for something else: bad ACL, overwrite etc.

powershell error when key is not present

i have made my script
$officeversion = reg query "HKEY_CLASSES_ROOT\Outlook.Application\CurVer"
if ($officeversion -eq " (Default) REG_SZ Outlook.Application.16"){
Write-Host "Office 2016"
Exit 0
}
if ($officeversion -eq " (Default) REG_SZ Outlook.Application.15"){
Write-Host "Office 2013"
Exit 0
}
if ($officeversion -eq " (Default) REG_SZ Outlook.Application.14"){
Write-Host "Office 2010"
Exit 0
}
if ($officeversion -eq " (Default) REG_SZ Outlook.Application.12"){
Write-Host "WARNING: Office 2007"
Exit 1010
}
if ($officeversion -eq " (Default) REG_SZ Outlook.Application.11"){
Write-Host "ALERT: Office 2003"
Exit 1010
}
if ($officeversion -eq " (Default) REG_SZ Outlook.Application.10"){
Write-Host "ALERT: Office XP"
Exit 1010
}
else {
Write-Host "No Office Installed"
Exit 0
}
but when they key is not present, im getting the following error:
reg : ERROR: The system was unable to find the specified registry key or value.
At C:\Users\syslocal\Desktop\RepopulateTDL2.ps1:1 char:18
+ ... iceversion = reg query "HKEY_CLASSES_ROOT\Outlook.Application\CurVer"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (ERROR: The syst...y key or value.:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
does anyone have a suggestion that I can suppress this error if the key doesn't exist?
Thank You
You can use PowerShells Registry provider to access the registry.
New-PSDrive -PSProvider Registry -Root HKEY_CLASSES_ROOT -Name HKCR
Then you can use commands like Get-Item, Get-ChildItem and Test-Path like you would on regular files and directories. That way you can use PowerShells error handling like the -ErrorAction parameter or try / catch blocks and all the good stuff.
Get-Item HKCR:\Outlook.Application\CurVer -ErrorAction Ignore
On a side note: for your script above, you should look into the switch statement, instead of using many if statements.
As others will no doubt point out, and you may already even be aware, using CMD commands in PowerShell is not the way PowerShell was meant to be used. It is more than capable of running registry queries with its own toolset. The main reason you want to stick to PowerShell cmdlets whenever possible is so that you don't have to worry about situations like this, where the program returns the error to the same stream as the successful output and you as a coder have to account for it. The advantage of PowerShell cmdlets is that different outputs are sent to different streams. See Guide. I would suggest moving to something like this if at all possible:
Get-ChildItem -Path "Registry::HKEY_CLASSES_ROOT\Outlook.Application\CurVer"
As I do understand that there are occasions where best practice is not possible, the solution you want to look to is called Try {} Catch {}. Now I'm not able to recreate your error message but I have gotten as close as I can. See example:
Try {
$officeversion = reg query "HKEY_CLASSES_ROOT\Outlook.Application\CurVer"
}
Catch [System.Management.Automation.ItemNotFoundException] {
# Registry path was not found
}
Catch {
# Some other error was thrown
}
Finally {
# Cleanup actions
# This step happens regardless of whether or not an error was thrown
}
See here for good documentation about Try {} Catch {}
Redirect stderr to $null
$officeversion = reg query "HKEY_CLASSES_ROOT\Outlook.Application\CurVer" 2> $null
this kinda works,
thanks for directing me in the correct directions i end up with
$officeversion = (get-itemproperty -ErrorAction Ignore -literalpath HKCR:\Outlook.Application\CurVer).'(default)'
if ($officeversion -eq "Outlook.Application.16"){
Write-Host "Office 2016"
Exit 0
}
if ($officeversion -eq "Outlook.Application.15"){
Write-Host "Office 2013"
Exit 0
}
if ($officeversion -eq "Outlook.Application.14"){
Write-Host "Office 2010"
Exit 0
}
if ($officeversion -eq "Outlook.Application.12"){
Write-Host "WARNING: Office 2007"
Exit 1010
}
if ($officeversion -eq "Outlook.Application.11"){
Write-Host "ALERT: Office 2003"
Exit 1010
}
if ($officeversion -eq "Outlook.Application.10"){
Write-Host "ALERT: Office XP"
Exit 1010
}
if ($officeversion -eq "Outlook.Application.10"){
Write-Host "ALERT: Office XP"
Exit 1010
}
else {
Write-Host "No Office Installed"
Exit 0
}
i will still look to the switch statement later
you could test if key is present or not with
Test-path "HKLM:\software\classes\outlook.application\curver"
you ll have statement to $true or false

file check script doesn't raise critical flag when file doesn't exist

I write a little script in PowerShell for Nagios that check if file exists.
If it exists the status should be "ok", and if not it should be "critical".
The problem is when the file does not exist the status is not "critical", it shows in Nagios as "unknown".
$path = "c:\test\test.txt"
$critical = 2
$ok = 0
if (-not (Test-Path $path)) {
Write-Host "file not exists"
exit $critical
} else {
Write-Host "file exists"
exit $ok
}
There's nothing wrong with your code, although I'd probably streamline it like this:
$path = "c:\test\test.txt"
$fileMissing = -not (Test-Path -LiteralPath $path)
$msg = if ($fileMissing) {'file does not exist'} else {'file exists'}
Write-Host $msg
exit ([int]$fileMissing * 2)
Your problem is most likely with the way you're executing the script. If you run the script using the -Command parameter, like this:
powershell.exe -Command "&{& 'C:\path\to\your.ps1'}"
or like this:
cmd /c echo C:\path\to\your.ps1 | powershell.exe -Command -
the return value is 1 if an error occured, or 0 otherwise, regardless of what exitcode you set.
To have PowerShell return the correct exit code you need to add an exit $LASTEXITCODE to the command string:
powershell.exe -Command "&{& 'C:\path\to\your.ps1'; exit $LASTEXITCODE}"
or call the script using the -File parameter:
powershell.exe -File "C:\path\to\your.ps1"

Powershell with Shutdown command error handling

My shutdown script using the Shutdown -R command to do a mass reboot of machines. If the Shutdown -R throws a error like "RPC Service Unavailable, or access denied" I can't catch it or just don't know how to. Can someone help? I don't want to use Restart-Computer in powershell since you can't delay the reboot and can't add comments.
foreach($PC in $PClist){
ping -n 2 $PC >$null
if($lastexitcode -eq 0){
write-host "Rebooting $PC..." -foregroundcolor black -backgroundcolor green
shutdown /r /f /m \\$PC /d p:1:1 /t 300 /c "$reboot_reason"
LogWrite "$env:username,$PC,Reboot Sent,$datetime"
} else {
write-host "$PC is UNAVAILABLE" -foregroundcolor black -backgroundcolor red
LogWrite "$env:username,$PC,Unavailable/Offline,$datetime"
}
}
If PowerShell Remoting is enabled on $PC something like this might work:
Invoke-Command -Computer $PC { shutdown /r /f /d p:1:1 /t 300 /c $ARGV[0] } `
-ArgumentList $reboot_reason
The -Computer option takes an array of names/IPs.
If you want to stick with your approach and just catch errors from shutdown.exe, evaluate $LastExitCode after the command:
shutdown /r /f /m \\$PC /d p:1:1 /t 300 /c "$reboot_reason" 2>$null
if ($LastExitCode -ne 0) {
Write-Host "Cannot reboot $PC ($LastExitCode)" -ForegroundColor black `
-BackgroundColor red
} else {
LogWrite "$env:username,$PC,Reboot Sent,$datetime"
}
2>$null suppresses the actual error message, and the check on $LastExitCode triggers the success/failure action.