Try/Catch Example - powershell

I understand Stackoverflow isn't a write your code for you forum, absolutely, but I'm finding it very difficult to find a good example of try/catch proper usage in Powershell. I have read up on fundamentals, and understand the theoretical concept, but execution I'm struggling with.
Here is a simple script that queries Active Directory:
do {
clear
Import-Module active*
"============ WhoIs Lookup ============="
Write-Host ""
$who = Read-Host "WhoIs";
$req = Get-ADUser -Identity $who
Write-Host ''
Write-Host "$who is " -NoNewline
Write-Host $req.Name -ForegroundColor Cyan
pause
} while ($run =1)
An example error is:
Get-ADUser : Cannot find an object with
identity: '5621521' under: 'DC=dcsg,DC=com'.
At C:\Tools\CSOCTools\Who_Is\whoIs.ps1:10
char:12
+ $req = Get-ADUser -Identity $who
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFoun
d: (5621521:ADUser) [Get-ADUser], ADIde
ntityNotFoundException
+ FullyQualifiedErrorId : ActiveDirecto
ryCmdlet:Microsoft.ActiveDirectory.Mana
gement.ADIdentityNotFoundException,Micr
osoft.ActiveDirectory.Management.Comman
ds.GetADUser
How would I catch this User Not Found error?

Simple example:
try {
Get-ADUser -Identity “bleh”
}
catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException]
{
Write-Warning “AD computer object not found”
}
For your case:
do {
clear
Import-Module active*
"============ WhoIs Lookup ============="
Write-Host ""
$who = Read-Host "WhoIs";
try {
$req = Get-ADUser -Identity $who
}
catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException]
{
Write-Warning “AD user object not found”
Write-Host ''
Write-Host "$who is " -NoNewline
Write-Host $req.Name -ForegroundColor Cyan
}
pause
} while ($run =1)
Edit: I put the Write-Host into the catch as you're eventually trying to reference to NULL when there's no object.

I got a very good example from here. For the exception types after the Catch (where I have two of them) I just grabbed them straight from the error message you provided. I haven't tried this out many times in my experience, let me know if it works for ya!
Try
{
do {
clear
Import-Module active*
"============ WhoIs Lookup ============="
Write-Host ""
$who = Read-Host "WhoIs";
$req = Get-ADUser -Identity $who
Write-Host ''
Write-Host "$who is " -NoNewline
Write-Host $req.Name -ForegroundColor Cyan
pause
} while ($run =1)
}
Catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException],[Microsoft.ActiveDirectory.Management.Commands.GetADUser]
{
# Error message here
}

Related

Statements has no effect (if/else) in PS

I'm working on a script that gets executed only if X account is found, but is not working as intended the if/else statements get bypassed and the code gets executed anyways. What am i doing wrong?
$Account = "XXXX"
Get-LocalUser -name $Account
if (($Account) -eq $true) {
} else {
Write-host -foreground cyan "I found it"
}
exit
If i ran the script as is it will output the text on the console even tho "XXX" account is not present, could something like that can be done?
This should do it:
$Account = "XXXX"
$AccountObject=Get-LocalUser -name $Account -ErrorAction SilentlyContinue
if (($AccountObject)) {
Write-host -foreground cyan "I found it"
} else {
Write-host -foreground cyan "No luck"
}
The issue with the sniplet provided - the return of Get-LocalUser was not used. Instead you were using a string value which is always set therefore true - as you set it to 'XXXX' in your first line.
As Bill_Stewart explains, the reason that the else block is reached is because ($Account) -eq $true evaluates to $false unless the account name is "true".
In order to test whether Get-LocalUser succeeded or failed to retrieve the user account, you can instead inspect the $? automatic variable - it will have a value of $false only if the previous command threw an error:
$AccountName = "nonExistingUser"
# Try to fetch existing user account, don't show any errors to the user
$UserAccount = Get-LocalUser -Name $AccountName -ErrorAction SilentlyContinue
# Test if the call was successful
if($?) {
Write-Host "Found account named '$AccountName'!" -ForegroundColor Cyan
$UserAccount
} else {
Write-Host "No account named '$AccountName' was found ..." -ForegroundColor Magenta
}

How can I turn this exceptional error message into my custom message in PowerShell?

I am writing the script which should validate the user in the active directory and gets some AD information. I am struggling with the error handling in this script:
$user = (Read-Host -Prompt 'Enter your network id').ToUpper()
#check if the user exists in the AD database
$userid= Get-ADUser $user | Select SamAccountName
$userid = $user
if (($user -match $userid)) {
Write-Host $user "exists in AD"
}else{
write-host "user cannot be found"
}
If someone who uses the script will put incorrect userId (which doesn't exist in AD), the script will throw an error message :
Get-ADUser : Cannot find an object with identity: 'DUMMY' under: 'DC=company,DC=com'.
At line:9 char:11
+ $memoid = Get-ADUser $user | Select SamAccountName
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (DUMMY:ADUser) [Get-ADUser], ADIdentityNotF
oundException
+ FullyQualifiedErrorId : ActiveDirectoryCmdlet:Microsoft.ActiveDirectory.Management.
ADIdentityNotFoundException,Microsoft.ActiveDirectory.Management.Commands.GetADUser
Even though incorrect userID was entered, I receive
= DUMMY exists in AD
How can I turn this exceptional error message into my custom message - "The user doesn't exist in AD"? Thank you in advance
For this, it is better not to use the -Identity parameter (which you imply in your code by using Get-ADUser $user)
Try
$userID = Read-Host -Prompt 'Enter your network id'
# check if the user exists in the AD database
# this will either return an ADUser object or $null
$user = Get-ADUser -Filter "SamAccountName -eq '$userID'" -ErrorAction SilentlyContinue
if ($user) {
Write-Host "$($user.SamAccountName) exists in AD" -ForegroundColor Green
}
else{
Write-Host "user $($user.SamAccountName) cannot be found" -ForegroundColor Red
}
You need to catch the exceptions using try/catch
In the error message, it is telling that "cannot find that object"
So, your first approach is to check whether the user exists or not in the If statement and then put them in try/catch like below
try{
$user = (Read-Host -Prompt 'Enter your network id').ToUpper()
#check if the user exists in the AD database
$userid= Get-ADUser $user | Select SamAccountName
$userid = $user
if (($user -match $userid)) {
Write-Host $user "exists in AD"
}else{
write-host "user cannot be found"
}
}
catch
{
#Your Custom Message in case of the error is coming in Std Out . $_.Exception.Message will catch the exact error message.
"Error Message: "+ "$_.Exception.Message"
}
The statement: $userid = $user needs to be deleted. As is it insures that you always have a match.
Next place your call to Get-ADUser in a Try/Catch construct to catch the error.
$user = (Read-Host -Prompt 'Enter your network id').ToUpper()
#check if the user exists in the AD database
Try {
$userid= Get-ADUser $user -EA "STOP" | Select SamAccountName
Write-Host $user "exists in AD"
}
Catch {
#*** Process errors here ***
write-host "user cannot be found"
Exit
}
#Continue your script here
Note: I don't have access to a Server so this is untested but should work in theory!
HTH

How do I delete user in Active Directory through Powershell?

I have to delete users from my AD through Powershell. Powershell has to ask me who I want to delete, once I type in the username it should delete the account. Also when Powershell succesfully deleted the account or it should give me a message.
I'm kind of noob at this, but here is my code:
function aduser-remove($userremove){
Remove-ADUser -Identity $delete
if ($delete -eq $userremove){
return $true
}
else {
return $false
}
}
$delete = Read-host "Which user do you want to delete? (Type in username)."
aduser-remove $delete
if ($userremove -eq $true){
Write-Host $delete "deleted succesfully!" -ForegroundColor Green
}
elseif ($userremove -eq $false){
Write-Host "An error occured by deleting" $delete -ForegroundColor Red
}
else {
Write-Host $delete "does not exist." -ForegroundColor DarkGray
}
The result here is that Powershell does ask if I want to delete the account and it works. But Powershell keeps giving me the else message instead of the if message. Deleting the account was succesfull.
I have no idea what to now or if I'm missing something (I bet I am otherwise it would work).
I hope you guys can help me!
As commented, your code uses variables in places where they do not exist.
Also, I would recommend trying to find the user first and if you do, try and remove it inside a try/catch block, as Remove-ADUser creates no output.
Below a rewrite of your code. Please note that I have changed the name of the function to comply with the Verb-Noun naming convention in PowerShell.
function Remove-User ([string]$userremove) {
# test if we can find a user with that SamAccountName
$user = Get-ADUser -Filter "SamAccountName -eq '$userremove'" -ErrorAction SilentlyContinue
if ($user) {
try {
$user | Remove-ADUser -ErrorAction Stop -WhatIf
return $true
}
catch {
return $false
}
}
# if we get here, the user does not exist; returns $null
}
$delete = Read-host "Which user do you want to delete? (Type in username)."
# call your function and capture the result
$result = Remove-User $delete
if ($result -eq $true){
Write-Host "User $delete deleted succesfully!" -ForegroundColor Green
}
elseif ($result -eq $false){
Write-Host "An error occured while deleting user $delete" -ForegroundColor Red
}
else {
Write-Host "$delete does not exist." -ForegroundColor DarkGray
}
Note also that I have put in the -WhatIf switch. This switch ensures you will only get a message of what would happen. No user is actually deleted. Once you are satisfied the code does what you want, remove the -WhatIf switch.
Hope that helps
If you have rsat installed https://www.microsoft.com/en-us/download/details.aspx?id=45520
remove-aduser https://learn.microsoft.com/en-us/powershell/module/addsadministration/remove-aduser?view=win10-ps

Using Read-host with Input validation and TRAP

I'm just a typical admin trying to make a simple script for some IT assistants in remote offices, to make domain joins easier while minimizing potential errors. The script's end game is to run the one-liner command Add-Computer -DomainName $DomainToJoin -OUPath $LocDN -NewName $WS_NewName -Restart.
But the whole script is supposed to include input validation for the computer's new name as well as for the target OU for the two-letter code for the remote office.
Googling for code snippets for days, esp. from sites like yours, was very helpful. But the problem I have now is I couldn't find the right codes to combine Read-Host , input length validation, and TRAP to work together without losing the value for my variables.
Pardon my coding as obviously I'm no real PS scripter, and I know the wrong portions of it are very basic. I would want to spend more time if I had the luxury, but I would really so much appreciate it if you could point me in the right direction.
Thank you so much in advance.
Please see my code below:
# START: Display name and purpose of invoked script
$path = $MyInvocation.MyCommand.Definition
Clear-Host
Write-Host $path
Write-Host " "
Write-Host "This script will allow you to do the following in a single-step process:"
Write-Host "(1) RENAME this computer"
Write-Host "(2) JOIN it to MYDOMAIN"
Write-Host "(3) MOVE it to a target OU"
Write-Host "(4) REBOOT"
Write-Host " "
Pause
# Function: PAUSE
Function Pause ($Message = "Press any key to continue . . . ") {
if ((Test-Path variable:psISE) -and $psISE) {
$Shell = New-Object -ComObject "WScript.Shell"
$Button = $Shell.Popup("Click OK to continue.", 0, "Script Paused", 0)
}
else {
Write-Host -NoNewline $Message
[void][System.Console]::ReadKey($true)
Write-Host
}
Write-Host " "
}
# Function: Define the parameters
Function Define-Parameters {
# Specify new computer name, with validation and TRAP
$WS_NewName = $null
while ($null -eq $WS_NewName) {
[ValidateLength(8,15)]$WS_NewName = [string](Read-Host -Prompt "NEW NAME of computer (8-15 chars.)" )
TRAP {"" ;continue}
}
Write-Host " "
# Domain to join.
$DomainToJoin = 'mydomain.net'
# Specify the target OU, with validation and trap
$baseOU='OU=Offices OU,DC=mydomain,DC=net'
$OU2 = $null
while ($null -eq $OU2) {
[ValidateLength(2,2)]$OU2 = [string](Read-Host -Prompt 'Target OU (TWO-LETTER code for your office)' )
TRAP {"" ;continue}
}
Write-Host " "
$LocDN = "OU=$OU2,$baseOU"
}
# Function: Summary and confirmation screen for defined parameters.
Function Confirm-Parameters {
Write-Host "==========================================================================="
Write-Host "Please confirm that you are joining this computer to
$DomainToJoin (MYDOMAIN)"
Write-Host "with the following parameters:"
Write-Host ""
Write-Host ""
Write-Host "Computer's NEW NAME: $WS_NewName"
# Write-Host "Domain to Join: $DomainToJoin"
Write-Host "TARGET mission OU: $OU2"
}
# Call Define-Parameters Function
Define-Parameters
# Call Confirm-Parameters Function
Confirm-Parameters
<#
Some more code here
#>
# FINAL COMMAND if all else works: Join the computer to the domain, rename it, and restart it.
# Add-Computer -DomainName $DomainToJoin -OUPath $LocDN -NewName $WS_NewName -Restart
In your code, you have a lot of things defined very strangely. Your functions create a new scope and the variables you're trying to define therein will disappear after calling them unless you change the variable scope (in this case, to $script: or $global:). Also, to use functions, you need to define them first (your Pause doesn't actually do anything)
Here's something you can do with your Define-Parameters function (I suggest looking at Get-Verb)
# Domain to join.
$DomainToJoin = 'mydomain.net'
# Function: Define the parameters
Function Get-Parameters {
do {
$global:WS_NewName = Read-Host -Prompt 'NEW NAME of computer (8-15 chars)'
} until ($WS_NewName.Length -gt 7 -and $WS_NewName.Length -lt 16)
''
do {
$global:OU2 = Read-Host -Prompt 'Target OU (TWO-LETTER code for your office)'
} until ($OU2 -match '^[a-z]{2}$')
''
$OU2 = "OU=$global:OU2,OU=Offices OU,DC=mydomain,DC=net"
}
I'd strongly recommend moving away from the ISE to do your testing and test in an actual powershell console.
Perhaps Try/Catch block instead of trap?
Try {
[ValidatePattern('^\w{8,15}$')]$compname=read-host 'enter comp name' -ErrorAction Stop
[ValidatePattern('^\w{2}$')]$OU=read-host 'enter OU name' -ErrorAction Stop
}
Catch {
$ErrorMessage = $_.Exception.Message
$ErrorLineNumber = $_.InvocationInfo.ScriptLineNumber
$ErrorCommandName = $_.InvocationInfo.InvocationName
Write-Error -Message "The error message was: <$ErrorMessage>, script line: <$ErrorLineNumber>, command name: <$ErrorCommandName>"
exit 255
}

Continue execution on Exception

Below is the script I want to execute. The issue here is once an exception occurs it stops executing, I used continue in the catch block but that did not work. How do I get it working even after an exception occurs it should loop in foreach.
I also used a while($true) loop but that went into infinite loop. How to go about it?
$ErrorActionPreference = "Stop";
try
{
# Loop through each of the users in the site
foreach($user in $users)
{
# Create an array that will be used to split the user name from the domain/membership provider
$a=#()
$displayname = $user.DisplayName
$userlogin = $user.UserLogin
# Separate the user name from the domain/membership provider
if($userlogin.Contains('\'))
{
$a = $userlogin.split("\")
$username = $a[1]
}
elseif($userlogin.Contains(':'))
{
$a = $userlogin.split(":")
$username = $a[1]
}
# Create the new username based on the given input
$newalias = $newprovider + "\" + $username
if (-not $convert)
{
$answer = Read-Host "Your first user will be changed from $userlogin to $newalias. Would you like to continue processing all users? [Y]es, [N]o"
switch ($answer)
{
"Y" {$convert = $true}
"y" {$convert = $true}
default {exit}
}
}
if(($userlogin -like "$oldprovider*") -and $convert)
{
LogWrite ("Migrating User old : " + $user + " New user : " + $newalias + " ")
move-spuser -identity $user -newalias $newalias -ignoresid -Confirm:$false
LogWrite ("Done")
}
}
}
catch {
LogWrite ("Caught the exception")
LogWrite ($Error[0].Exception)
}
You use try {...} catch {...} when you want to handle errors. If you want to ignore them, you should set $ErrorActionPreference = "Continue" (or "SilentlyContinue") as #C.B. suggested, or use -ErrorAction "SilentlyContinue" for the particular operation raising the error. If you want to handle errors from a certain instruction, you'd put that instruction in the try {...} catch {...} block, not the entire loop, e.g.:
foreach($user in $users) {
...
try {
if(($userlogin -like "$oldprovider*") -and $convert) {
LogWrite ("Migrating User old : " + $user + " New user : " + $newalias + " ")
move-spuser -identity $user -newalias $newalias -ignoresid -Confirm:$false
LogWrite ("Done")
}
} catch {
LogWrite ("Caught the exception")
LogWrite ($Error[0].Exception)
}
}
You seem to have placed the "catch" outside the loop body, so that aborts the loop. Put the catch inside the loop
Modified the code as below. Used the following piece of code after move-spuser -identity $user -newalias $newalias -ignoresid -Confirm:$false
if($?)
{
LogWrite ("Done!")
LogWrite (" ")
}
else
{
LogWrite ($Error[0].ToString())
LogWrite (" ")
}
Something that worked for me is to set the $ErrorActionPreference variable to stop, and then reset it back to continue in the catch block:
$e = $ErrorActionPreference
$ErrorActionPreference="stop"
try
{
#Do Something that throws the exception
}
catch
{
$ErrorActionPreference=$e
}
$ErrorActionPreference=$e;