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
Related
I am not quite sure how to explain my problem, but I have a function that installs Office, imagine the person that runs this script does not have internet connection or does not have enough space on her hard drive. I have the XML file set to hide the setup interface so the user can't see the installation process. Just to be clear all my code works fine, just want add this feature so that if something goes wrong while the user runs the script I know where the error was.
This is my function:
Function Install-Office365OfficeProducts{
Write-Host ""
Start-Sleep -Seconds 5
Write-Host "Installing Office 365 ProPlus..."
# Installing Office 365 ProPlus
Install-Office365Product -path "$PSScriptRoot\setup.exe" -xmlPath "$PSScriptRoot\InstallO365.xml"
This is what I have tried:
if (Install-Office365OfficeProducts -eq 0) {
Write-Host "FAILED"}
I am very confused, I thought that a function that runs with no error returns 1 and when it runs with errors returns 0.
Also have tried to put the code like this:
try {
Install-Office365Product -path "$PSScriptRoot\setup.exe" -xmlPath "$PSScriptRoot\InstallO365.xml"
} catch {
Write-Host "Failed!"
}
EDIT:
Basically i want to be shown an error if the Office setup is not finished...
#Thomas
Function Install-Office365Product{
Param (
[string]$path,
[string]$xmlPath
)
$arguments = "/configure `"$xmlPath`""
try{
Start-Process -FilePath "$path" -ArgumentList "$arguments" -Wait -NoNewWindow -ErrorAction Stop
}catch{
Write-Host "It was not possible to install the product!"
}
}
Your try/catch-block inside Install-Office365OfficeProducts is useless, because Install-Office365Product will not throw anything, except you pass wrong arguments. The try/catch-block inside Install-Office365Product will most likely also not catch anything. But you can of course evaluate the return code of your installer called with Start-Process:
function Install-Office365Product {
Param (
[string]$path,
[string]$xmlPath
)
$arguments = "/configure `"$xmlPath`""
$process = Start-Process -FilePath "$path" -ArgumentList "$arguments" -Wait -PassThru -NoNewWindow
if ($process.ExitCode -eq 0) {
Write-Host "Installation successful"
} else {
Write-Host "Installation failed"
}
}
Instead of writing to stdout, you can of course also throw an exception and handle it later in a higher function.
I am working on a script that will delete App-V keys stored in the registry. When a user opens an application, it creates a key within the following location:
HKLM\SOFTWARE\Microsoft\AppV\MAV\Configuration\Packages\**PackageID**\UserConfigEx\**SID**
The PackageID and the SID are unique each time and I want to be able to delete the SID subkey within each PackageID key.
The user will enter the SID and then I would like to use a wildcard (if possible) to navigate into each Package ID which is present.
So far I have the following:
#Take user input
$SID = Read-Host "Please enter users SID"
$computer = Read-Host "Please enter computer name"
#Test connection
Write-Host "Connecting to $computer"
if (Test-Connection -ComputerName $computer -Quiet -BufferSize 16 -Count 1) {
#Connect to registry and delete key
try
{
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey(‘LocalMachine’, $computer)
$regKey = $reg.OpenSubKey(“HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\AppV\\MAV\\Configuration\\Packages\\*\\UserConfigEx\\$SID”,$true )
if ($regkey.GetValue(“$SID”))
{
$regKey.DeleteValue(“$SID”)
Write-Host
Write-Host "$SID key deleted successfully" -ForegroundColor Green
}
else
{
Write-Host
Write-Host "No keys with this SID exist." -ForegroundColor Red
}
} catch {
$ErrorMessage = $_.Exception.Message
Write-Host "Unable to connect to $computer. Error: $($ErrorMessage)." -ForegroundColor Red
}
} else
{
Write-Host "Unable to connect to $computer. Please ensure correct computer name / IP address has been entered correctly." -ForegroundColor Red
}
If I run this I receive:
You cannot call a method on a null-valued expression.
At line:51 char:9
+ if ($regkey.GetValue(“$SID”))
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
I am using some of the script which I received help with here to remotely connect to the machine.
The .NET registry API doesn't support wildcards (*) in key paths.
As a result, the $regKey.GetValue() failed, because $regKey = $reg.OpenSubKey(...) returned $null due to not finding a key, and calling a method on $null always results in the error message quoted in the question.
By contrast, PowerShell's registry provider, via the *-Item* cmdlets, does, but you need PowerShell remoting in order to use it remotely.
PowerShell remoting is enabled by default on Windows Server 2012 and above; on older OS versions you can enable it by running Enable-PSRemoting on the target machine(s) (requires PSv3+).
With PowerShell remoting enabled, you need to wrap your code in an Invoke-Command -ComputerName <name> { ... } call (to which you may have to pass credentials too).
If enabling PowerShell remoting is not an option, you must emulate wildcard-based matching via a nested loop based on per-element wildcard matching of the results from .GetSubkeyNames().
As an aside: you never need to escape \ as \\ in PowerShell strings; PowerShell uses ` as the escape character inside "...", so the only character you need to escape there is ` itself, as ``.
A PowerShell remoting-based solution:
Note that Invoke-Command -ComputerName ... must be called from an elevated session (Run As Administrator):
try {
Invoke-Command -ErrorAction Stop -ComputerName $computer {
# Define wildcard-based path.
$keyPath = "registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\AppV\MAV\Configuration\Packages\*\UserConfigEx\$SID"
# See if it matches any keys.
if (Test-Path $keyPath) {
# Note: I'm assuming you want to remove the entire *key*.
# To only remove a key's *value*, use Remove-ItemProperty.
Remove-Item -Path $keyPath
} else {
Write-Warning "No keys with SID $SID exist."
}
}
} catch [System.Management.Automation.Remoting.PSRemotingTransportException] {
# Note: Depending on the specifics of your Invoke-Command call, the reason may
# be permissions-related; when in doubt, examine $_
Write-Warning "Unable to connect to $computer. Please ensure correct computer name / IP address has been entered correctly:`n$_"
} catch {
# Other, unexpected failure.
Throw
}
Looks like an ascii vs unicode quotation mark issue:
You have:
$regkey.GetValue(“$SID”)
which should be replaced with:
$regkey.GetValue("$SID")
I am having a bit of an annoying problem that I just can't seem to find the answer to. I am using a powershell script to uninstall programs and a batch file to run the script on the programs I want removed in a particular order. I found the script on the web and it works perfectly on my test machine. The problem begins when I run the script on another machine.
This is the error I get:
Error formatting a string: Input string was not in a correct format..
At C:\Users\currentuser\Desktop\uninstallScript.ps1:60 char:1
+ &msiexec `/qn `/x `{$stringer`}
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: ({{0}}}:String) [],
RuntimeException
+ FullyQualifiedErrorId : FormatError
And here is the PowerShell script:
###########################################
######## Written by DC 2012-02-13#########
###########################################
<#
.SYNOPSIS
Uninstalls software by only passing the Software Title.
Should work with all msiexec string uninstallers.
For uninstall commands that end in uninstall.exe or helper.exe a "/S" is
used as a switch.
.PARAMETER DisplayName
The complete or partial name of the software being uninstalled. Must appear
as shown in add / remove programs (case insenstive).
.EXAMPLE
Uninstall-Program Java
Will search the registry and uninstall all instances of Java from a machine.
#>
[cmdletBinding()]
Param
(
[String]$DisplayName = $(throw "DisplayName is Required")
)
Set-Variable -Name ThirtyMachine -Value
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" -Option Constant
Set-Variable -Name SixtyMachine -Value
"HKLM:\SOFTWARE\WOW6432NODE\Microsoft\Windows\CurrentVersion\Uninstall" -
Option Constant
Set-Variable -Name ThirtyUser -Value
"HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" -Option Constant
Set-Variable -Name SixtyUser -Value
"HKCU:\SOFTWARE\WOW6432NODE\Microsoft\Windows\CurrentVersion\Uninstall" -
Option Constant
$regs = $ThirtyMachine,$SixtyMachine,$ThirtyUser,$SixtyUser
foreach ($reg in $regs)
{
if(Test-Path $reg)
{
$SubKeys = Get-ItemProperty "$reg\*"
}
else
{
$SubKeys = $null
}
foreach($key in $SubKeys)
{
if($key.DisplayName -match "$DisplayName")
{
Write-Host "Found Software " $key.DisplayName
if($key.UninstallString -match "^msiexec")
{
$startGUID = $key.UninstallString.IndexOf("{") + 1
$endGuid = $key.UninstallString.IndexOf("}") - $startGUID
$stringer = $key.UninstallString.Substring($startGUID,$endGuid)
Write-Host "Uninstaller Known, now uninstalling"
&msiexec `/qn `/x `{$stringer`}
}
if($key.UninstallString.Replace('"',"") -match 'uninstall.exe\Z' -or
$key.UninstallString.replace('"',"") -match 'helper.exe\Z' )
{
$stringer = $key.UninstallString.Replace('"',"")
if(Test-Path $stringer )
{
Write-Host "Possible Uninstaller found. Trying" $key.UninstallString "/S"
&$stringer /S
}
}
}
}
}
I debugged the code in PowerShell ISE to verify that the variable $stringer contained the correct value. So I believe this has something to do with the version of powershell being run on the 2 machines. The test machine, where the script works, is version 2 whereas the other machine, where I get the error, is version 4. I've barely begun to learn powershell so I am scratching my head at this one. Hopefully it is simple to resolve. I appreciate any help offered.
The escape characters ``` in your msiexec call are causing the error and shouldn't be there. Here's a way to call it how you are properly:
& msiexec /qn "/x{$stringer}"
Alternatively, you can utilize Start-Process and wait on the process:
Start-Process -FilePath 'msiexec' -ArgumentList #('/qn','/x',"{$stringer}") -Wait
I's just a basic one, I am new to Powershell. Trying to get the statement below working.
$date = (Get-Date).AddDays(-1)
$currentdate = Get-Date -Format d
$check = Get-WinEvent -FilterHashtable #{LogName="CloudBackup";StartTime=$date;ID=3} *>$null
if ($check -eq $true) {
Write-Host "`nOK: Azure Backup was successful on $currentdate"
exit 0
} else {
Write-Host "`nCritical: Problem with Azure Backup - $currentdate"
exit 2
}
Specially if ($check -eq $true) doesn't seem to do what expected. As $check is checking for event ID 3 in the eventlog, if it's there it should return true, if not false. Unfortunately it's returning only false every time.
Could someone please advise? Is there a better way to do that?
$check = Get-WinEvent ... *>$null
Your redirection is suppressing all output, so $check always has the value $null, which is interpreted as $false in a boolean operation.
What you want to use is the automatic variable $? to check it if the last PowerShell operation was successful.
if ($?) {
Write-Host "OK: Azure Backup was successful on $currentdate"
exit 0
} else {
Write-Host "Critical: Problem with Azure Backup - $currentdate"
exit 2
}
i try to do error handling within my powershell script. but i always get an fatal. i tried a few things, e. g. try{ } catch{ } - but i did it not get to work.
any ideas or Solutions?
Function Check-Path($Db)
{
If ((Test-Path $Db) –eq $false) {
Write-Output "The file $Db does not exist"
break
}
}
It Returns:
Test-Path : Zugriff verweigert
In K:\access\access.ps1:15 Zeichen:6
+ If ((Test-Path $Db) -eq $false) {
+ ~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (K:\ss.mdb:String) [Test-Path], UnauthorizedAccessException
+ FullyQualifiedErrorId : ItemExistsUnauthorizedAccessError,Microsoft.PowerShell.Commands.TestPathCommand
Somewhat confusingly Test-Path actually generates an error in a number of cases. Set the standard ErrorAction parameter to SilentlyContinue to ignore it.
if ((Test-Path $Db -ErrorAction SilentlyContinue) -eq $false) {
I cannot answer directly. So this has to do:
I strongly disagree with your answer. Test-Path does show $false when you run it against a network share that is not accessible, but it will be false also (without Exception) when the Server is not reachable.
So your answer simply ignores anything but a reachable share.
What is neccessary however is a try-catch-block that handles this better:
[cmdletbinding()]
param(
[boolean]$returnException = $true,
[boolean]$returnFalse = $false
)
## Try-Catch Block:
try {
if ($returnException) {
## Server Exists, but Permission is denied.
Test-Path -Path "\\Exists\Data\" -ErrorAction Stop | Out-Null
} elseif ($returnFalse) {
## Server does not exist
Test-Path -Path "\\NoExists\Data\" -ErrorAction Stop | Out-Null
}
} catch [UnauthorizedAccessException] {
## Unauthorized
write-host "No Access Exception"
} catch {
## an error has occurred
write-host "Any other Exception here"
}
The really important part however is the ErrorAction on the Test-Path command, otherwise the exception will be wrapped around a system management error and is thus not catchable. This is in detail explained here:
PowerShell catching typed exceptions