Deleting mailboxes and a bit of fun with errors.

Background

Following on from my last post about creating shared mailboxes, this one is about deleting mailboxes. In addition, we will be removing some domain groups that are used to grant access to a shared mailbox. I also wanted to raise my game a bit about error checking – I have plenty of batch scripts that check “if ERRORLEVEL 1, stahp” (if it’s not 0, something’s not right). Not so much with my Powershell yet. Much more on this later.

Specifics

This script is intended to be used to delete shared or resource mailboxes and the access groups associated with them. The AD account should be ditched as well, and Microsoft have provided the Remove-Mailbox cmdlet to nicely do both at the same time.

The script is not intended to be used for normal user mailboxes in our environment, since we retain user accounts for departed users, indefinitely. Disabled, at least.

Assumptions:

  • The mailbox and associated AD account should both be deleted, since it is a defunct shared or resource mailbox.
  • Domain groups may exist which grant Send As and/or Send on Behalf rights to the mailbox (as well as Full Access). They are named with the format FAAS_[MBXName] or FAOB_[MBXName] and should be deleted.

Syntax

The script assumes that you are working in an Exchange PowerShell environment (e.g. the EMS), and have the appropriate rights to delete mailboxes in the Exchange org and modify AD users and groups. Your working directory is where the script is located.

To execute the script, run the following in the EMS console:

 .\rem-bus-mbx.ps1 -mbx [MBX_NAME]

Run the script rem-bus-mbx.ps1specifying the mailbox name (AD account name). This will:

  • Delete the AD account. The mailbox is detached and kept in its database until the mailbox purge cycle runs (30 days by default)
  • Check for the existence of the FAAS or FAOB groups and remove them if they’re in AD.

Code

## Syntax: .\rem-bus-mbx.ps1 -mbx [MBX_NAME]
param (
	[string]$mbx = $(throw "-mbx is required for mailbox name.")
)

Import-Module ActiveDirectory

# Delete mailbox and account
try {
	Remove-Mailbox $mbx -confirm:$false -ErrorAction Stop
	Write-Host $mbx "deletion successful." -foregroundcolor green
}
catch {
	Write-host $mbx "deletion failed - exiting." -foregroundcolor red
	Exit
}

# Remove FAAS and FAOB groups if they exist
$groups = @("FAAS_$mbx", "FAOB_$mbx")
foreach ($g in $groups) {
	try {
		$group = Get-ADGroup -Id $g
		Write-Host "Deleting " $group.Name "...
			try {
				Remove-ADGroup $group -confirm:$false -ErrorAction Stop
				Write-Host $group.Name "deletion successful." -foregroundcolor green
			}
			catch {
				Write-host $group.Name "deletion failed." -foregroundcolor red
			}
	}
	catch {
		Write-Host "Group" $g "does not exist - no action taken." -foregroundcolor cyan
	}
}

Results

Normal script execution comes up in pretty colours now, showing green for success, cyan for informational (such as a group that doesn’t exist not being deleted), and red for error. I am not at guru stage yet with error handling, and it doesn’t even dump the error text.

The image below shows the successful deletion of “MBX_PRINTERS”. At the same time, the FAAS group was deleted. An FAOB group did not exist, so it was skipped.

script_ok

Trying and trying and catching

So the method for seeing whether the AD groups exist (starting at line 19 in the code) is by using Get-ADGroup to “get” a group of the specified name in AD. Obviously if it exists, the object will return “True”. If not, then it’s “False”. The $group variable that will hold the group identified by Get-ADGroup remains unpopulated if it’s not found. To display a group name that wasn’t found and therefore not deleted), we return the current contents of $g variable that we used to iterate through the array of potential group names (since $group is empty!)

For the mailbox, we just cut to the chase and try to delete the mailbox at line 10 – if it doesn’t exist, it throws an error. Like most Powershell errors, it’s three lines of mostly useless text, and I wanted something that was both succinct and would enable us to do useful things like terminate the script (if it couldn’t find the mailbox), or go to the next loop of AD group deletion (if the first group was not found).

According to many, “try and catch” is the best way to trap errors in Powershell. However, as admitted by June Blender (working in the PS dev team and documenting this stuff!) in the 2nd comment below that article, there was no clear consensus on “best practice” for catching errors, and so it really still is a mish-mash. That whole thread is worth a read for the kind of issues that people run into using this method. You’ll find many more methods on the Internet with people describing ways of using the $? variable, try/catch and others.

Added to that, there are differences between “terminating” and “non-terminating” commands (don’t ask me to link to a clear explanation; I haven’t found one yet see comments below for an awesome and succinct explanation from Don Jones from Powershell.org). The Exchange console, since it’s essentially using a form of remote execution, pretty much seems to default to “non-terminating”. I think.

In the end, doing a nice try and catch, with all the “positive” actions in the Try block (that is, all the actions that should take place if the first action is OK/True), and the “OMG, stop this thing” actions in the Catch block seemed like the best thing to do. It has the extra advantage of suppressing error messages if the first parameter is erroring.

BUT.

If you write the code below, guess what happens if the mailbox you’re attempting to delete doesn’t exist? (Perhaps your colleague who never acknowledges issue tickets is working on the request and just deleted it.)

...
# Delete mailbox and account
try {
	Remove-Mailbox $mbx -confirm:$false
	Write-Host $mbx "deletion successful." -foregroundcolor green
}
catch {
	Write-host $mbx "deletion failed - exiting." -foregroundcolor red
	Exit
}
...

Do you expect it to run the Remove-Mailbox command – here I’m trying with a rubbish string called “rubbish” –  go “oops, no mailbox” and then skip down to the catch block, write the nice error message in red and then terminate?

NO, it does THIS.

script_err

So it’s not skipping down to the Catch block at all. It still continues to run the Try branch, finishes that (giving the three-line PS error text when it fails to find the mailbox, and false status in green) and then tries executing the remainder of the script. The “rubbish” groups don’t exist either, so no surprises there.

After some appreciable time hunting around, this awesome person called Shay Levi (whose babies I might well have had if I were that way inclined) provided the answer in a semi-obscure comment on Stackoverflow: the Remove-Mailbox cmdlet is non-terminating here, so you have to enforce it as terminating by using the -ErrorAction Stop parameter (the default -ErrorAction in any PS script is “Continue”). Then it breaks from the Try loop and jumps down to our Catch and proceeds as we expect. Finally.

err_fixed

There IS no mailbox called “rubbish” and now we’ve exited nicely with red error text – see line 10 in the Code section above for the -ErrorAction Stop. One day I may figure out how to give a more meaningful error. I recycled the same method for the AD group deletion (line 25). Instead of crashing out of the whole script, it simply exits from the line 19 foreach $groups iteration it’s currently on and moves to the next, as shown nicely in the Results section above.

So that was literally hours of “entertainment”, but I got something that actually works and error-checks at the same time.

Advertisements
This entry was posted in Exchange and tagged , . Bookmark the permalink.

2 Responses to Deleting mailboxes and a bit of fun with errors.

  1. Don Jones says:

    A “terminating” exception is one where the command cannot continue. Imagine running New-ADUser and not being able to contact a DC – that’s terminating. A “non-terminating” error is one the command can skip over and keep going with… imagine running Get-WmiObject against several computers, only one of which is unavailable on the network. -ErrorAction or $ErrorActionPreference controls what happens for a non-terminating error. Terminating errors always… terminate.

    So Remove-Mailbox is non-terminating because if it fails to remove ONE specified box, it could possibly continue trying others you’d specified. That’s often the case for commands that can process multiple objects in one go.

    What you’ve discovered is that you can’t “catch” a non-terminating error. That’s because the command is still running. You have to turn it into a terminating exception (-ErrorAction Stop) so that the command QUITS, and allows the shell to process the catch.

    So it’s best to structure your scripts so that any command where you’re trying to catch an error is only processing one thing. That way, if the thing explodes, you can STOP the command and catch the error, without “losing” anything else the command might have tried to do.

    • tmacstips says:

      That’s a fantastic explanation as to why there are the non-terminating errors – the logic (put that way) absolutely makes sense if your command may want to continue to run against others in a list.
      So thank you for the clarification and the confirmation that the -ErrorAction Stop is really what’s needed in that kind of instance!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s