Handling errors with ADSI - powershell

I'm working on a PowerShell script to change a local account name. Of course, the first step is to check that the account exists:
$user=[ADSI]"WinNT://$server/$oldName,user"
If the account exists, then no problem. But if it doesn't, then I get this error:
format-default : The following exception occurred while retrieving member >"distinguishedName": "The user name could not be found."
+ CategoryInfo : NotSpecified: (:) [format-default], ExtendedTypeSystemException
+ FullyQualifiedErrorId :
CatchFromBaseGetMember,Microsoft.PowerShell.Commands.FormatDefaultCommand
I can't figure out how to look for that error, report something like "$oldName not found" and continue on. From what I can tell, it isn't being thrown into an error variable, so I can't search for a "user name could not be found" string. Try-Catch-Finally seems to ignore the error.
I admit I'm weak on error-handling. It seems there's countless ways for something to fail, and my users always find new ones when using my scripts.

It seems like the command is actually throwing a terminating error. From about_preference_variables
"Neither $ErrorActionPreference nor the ErrorAction common parameter
affect how Windows PowerShell responds to terminating errors (those
that stop cmdlet processing)."
So when the command runs it is terminating the script even before it can move on to try and process a catch block.
Interestingly if you put it into a variable this behavior stops happening. I'd be curious to see if anyone has a better answer, but it looks like the solution from what I can see, would be an if statement based on the results of the variable.
$User = [ADSI]"WinNT://badserver/Name,user"
If (! $User.Name)
{
Throw "Issue retrieving user"
}
#Rest of script can continue here

You can check whether a username exists in this way
[ADSI]::Exists("WinNT://$Server/$UserName")
It returns a Boolean value. If user exists, you get true, otherwise false.

I solved a similar issue by wrapping the command in a script block and using Invoke-Command.
$ChangePassword = {([adsi]"WinNT://domain/$Username,user").ChangePassword($CurrentPassword, $NewPassword)}
try {
Invoke-Command -ScriptBlock $ChangePassword -ErrorAction Stop
}
catch {
# Error handling code
}

Related

How can I change the default PowerShell error for when a command needs elevation?

I have a lot of code in a PowerShell script that are mix of commands that need elevation to run and commands that don't, those that need elevation show errors in PowerShell console like:
"You don't have enough permissions to perform the requested operation"
and
"Requested registry access is not allowed."
is there a way to globally suppress only the kinds of errors that PowerShell shows due to lack of necessary privileges?
I thought about a function that checks for elevation and performs actions based on the result, like this:
https://devblogs.microsoft.com/scripting/use-function-to-determine-elevation-of-powershell-console/
Function Test-IsAdmin
{
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object Security.Principal.WindowsPrincipal $identity
$principal.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}
and
if(-NOT (Test-IsAdmin))
{ write-host "Skipping Admin command" }
else { $code }
but I don't know how to apply it globally to the whole script, so that commands that don't need elevation run, and those that need elevation show a custom message or skip that part silently.
another thing that can help my situation would be to find out if a PowerShell command needs elevation before actually running it and causing it to show errors in console due to lack of privileges.
It seems that errors stemming from a lack of privileges typically - but not necessarily - involve a System.UnauthorizedAccessException or System.Security.SecurityException .NET exception behind the scenes, whose name is then reflected as part of the .FullyQualifiedErrorId property of the resulting PowerShell error record, which is of type System.Management.Automation.ErrorRecord.
Assuming that this applies to all errors you care about, you can use a (rarely used anymore) trap statement as follows:
trap {
if ($_.FullyQualifiedErrorId -match 'UnauthorizedAccessException|SecurityException') {
Write-Warning "Skipping admin command ($($_.InvocationInfo.Line.Trim()))"
continue # Suppress the original error and continue.
}
# If the error was created with `throw`, emit the error and abort processing.
# SEE CAVEAT BELOW.
elseif ($_.Exception.WasThrownFromThrowStatement) { break }
# Otherwise: emit the error and continue.
}
# ... your script
Caveat:
If your script implicitly raises script-terminating errors - via -ErrorAction Stop or $ErrorActionPreference = 'Stop' - the above solution in effect turns them into statement-terminating errors and continues execution (only explicit script-terminating errors created with a throw statement are recognized as such in the code above, and result in the script getting aborted).
Unfortunately, as of PowerShell 7.2.x, there is no way to generally discover whether a given error is (a) non-terminating, (b) statement-terminating or (c) script-terminating (fatal).
See GitHub issue #4781 for a proposal to add properties to [System.Management.Automation.ErrorRecord] to allow such discovery in the future.

try catch block in another try catch block PowerShell [duplicate]

This question already has answers here:
Try/catch does not seem to have an effect
(7 answers)
Closed 3 years ago.
I am trying to handle setting a PowerShell variable from a registry key.
So I use a try{} catch {} to get rid of eventual errors in case the key doesn't exists. However, I still get the error output on console.
$ZZ_ConVTL = try { (Get-ItemProperty -path "HKCU:\Console" -name VirtualTerminalLevel).VirtualTerminalLevel } catch { "N/A" }
...
# Output:
Get-ItemProperty : Property VirtualTerminalLevel does not exist at path HKEY_CURRENT_USER\Console.
At C:\Users\Administrator\Documents\xxxx\xxxx.ps1:181 char:32
+ ... = try { (Get-ItemProperty -path "HKCU:\Console" -name VirtualTermi ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (VirtualTerminalLevel:String) [Get-ItemProperty], PSArgumentException
+ FullyQualifiedErrorId : System.Management.Automation.PSArgumentException,Microsoft.PowerShell.Commands.GetItemPropertyCommand
How can I handle and avoid this error from showing up in the console?
What your Get-ItemProperty call emits is a non-terminating error, whereas try / catch only catches terminating errors.
Non-terminating errors are far more common that terminating ones.
Use common parameter -ErrorAction Stop to promote (the first) non-terminating error generated by a cmdlet to a terminating one that try / catch handles.
You can generally achieve the same effect by setting preference variable
$ErrorActionPreference = 'Stop' beforehand, but note that doing so has no effect on calls to external programs and on functions implemented in modules.
See also:
The about_Try_Catch_Finally help topic.
A description of the fundamental error types in the context of guidance for command authors on when to emit a terminating vs. a non-terminating error: this answer.
A comprehensive overview of PowerShell's surprisingly complex error handling: this GitHub docs issue.

Powershell function with Try...Catch not making it into the function, because the parameter causes an error

I'm trying to use a function to confirm or deny whether or not we made it past a cmdlet without an error -- I'm running a bunch of AD/Exchange cmdlets and am storing/outputting the results to .csv at the end. I forgot to import the Exchange module, which worked to my benefit since it terminated in a way I wasn't expecting when I used Get-DistributionList.
I've tried using $? in place of the Try..Catch, forcing the EA to be stop, and storing the parameter in a variable first, but since the module isn't installed and the cmdlet isn't recognized it just stops the program.
Here's essentially what I want to do:
function Test-Success ($cmdlet){
try{
$cmdlet
"Y"
} catch {
"Err -- Perform manually."
}
}
Test-Success(Get-DistributionList)
But I get the following error, and the script stops:
Get-DistributionList : The term 'Get-DistributionList' is not recognized as the name of a cmdlet, function, script
file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct
and try again.
At line:46 char:10
+ Test-Success(Get-DistributionList)
+ ~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Get-DistributionList:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Worst case scenario, I can just place the try...catch in every time, as it seems to work that way (example:)
try {
Get-DistributionList
"y"
} catch {
"Err -- Perform manually"
}
Any help is appreciated!I'm hoping there is a workaround so that it won't terminate the program if a cmdlet fails in this way, but I'm not terribly familiar with PowerShell and my own searches have been inconclusive.
If you are just looking to see if the command exists, you should use Get-Command like #DimplesMcGibble suggested. If you are trying to execute the command, you can pass the command name as a string and use the invoke-operator
function Test-Success ($cmdlet){
try{
& $cmdlet
"Y"
} catch {
"Err -- Perform manually."
}
}
Test-Success 'Get-DistributionList'
You should be able to use Get-Command to check for the existence of a given cmdlet without the try\catch

Exception not caught when using PowerShell command line but caught as expected when using PowerShell ISE

I am running PowerShell 2.0.
I have a PowerShell script that adds a record to the database and returns the ID of the record added.
The record ID is returned as a property called new_deal_id within a DataRow object (called ResultSet).
If there is a problem on the database end it is possible that the new_deal_id property does not get set, or doesn't exist at all.
To counter this scenario I wrapped the reading of the property in a try/catch block as shown here.
try {
$ErrorActionPreference = "Stop"
$ResultSet = Read-DatabaseData -OdbcCommand $OdbcCommand -SqlQuery $Sql
$NewDealID = $ResultSet.new_deal_id
}
catch {
throw
}
finally {
$ErrorActionPreference = "Continue"
}
If I run the script using the PowerShell ISE or PowerGui the exception shown below gets caught when the property doesn't exist
Property 'new_deal_id' cannot be found on this object. Make sure that it exists.
At line:1 char:12
+ $ResultSet. <<<< newdeal
+ CategoryInfo : InvalidOperation: (.:OperatorToken) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : PropertyNotFoundStrict
However if I run the script from the PowerShell command line the exception does not get caught and the script continues as if no error occurred.
Why is the PowerShell command line not catching the exception when the property doesn't exist?
This is probably because you dont have strict mode enabled in the console you are running your script in. (Powershell and ISE use different profiles)
To enable strict mode use the Set-Strictmode cmdlet.
Example:
Set-StrictMode -Version latest

Using try/catch blocks with Lync 2010 cmdlets in powershell

I've written a function to wrap the the new-cssipdomain cmdlet with a try/catch block incase the sip domain already exists.
Code is:
function LHP-AddSIPDomain
{
param ( [string] $SIPDomain)
try
{
New-cssipdomain -id $SIPDomain
}
catch
{
Write-host "Lync specific exception occured adding SIP domain"
Write-host "Exception String:"+$_.Exception.Message
exit
}
}
LHP-AddSIPDOmain -SipDomain "Test206.com"
The output when the domain already exists is:
New-CsSipDomain : "SipDomain" with identity "Test206.com" already exists. To modify
the existing item, use the Set- cmdlet. To create a new item, use a different
identity. Parameter name: Identity
At S:\Scripts\LHP-AddSIPDomain.ps1:33 char:26
+ New-cssipdomain <<<< -id $SIPDomain
+ CategoryInfo : InvalidArgument: (Test206.com:String) [New-CsSipDomain],
ArgumentException
+ FullyQualifiedErrorId :
InvalidIdentity,Microsoft.Rtc.Management.Xds.NewOcsSipDomainCmdlet
This should be caught by the try/catch block.
I've tried adding the [system.exception] to the catch statement. I'ev also tried setting $erroraction=”Stop”. Neither made any different, the try/catch statement seems to be being ignored. I've used this type of code structure to capture errors from the new-aduser cmdlet and this seemed to work ok.
I have also considerd and tried using hte get-cssipdomin cmdlet first to check if the sip domain already exists, but I have a similar problem in that if you call get-cscsipdomain with a domain that doesn't exist it throws an error which I don't seem to be able to catch.
Any suggestions would be greatly appreciated.
TRY:
try
{
New-cssipdomain -id $SIPDomain -ERRORACTION SilentlyContinue
}
Maybe the command it self got a try/catch for errors.
You can perhaps have a look to this answer. It explains why try/catch is sometime not working.
Can't you just write :
$Res = New-cssipdomain -id $SIPDomain -ERRORACTION SilentlyContinue
And test the value of $Res ?
I guess the error you get is not a terminating error and that's why you can't catch it. Try to set the ErrorAction value to 'stop', that will make the error a terminating error and you'll be able to catch it in the catch block.