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
Posted in Exchange | Tagged , | 2 Comments

Sharing mailboxes … working with external account creation

Background

A common task in an Exchange environment is setting up shared mailboxes for multiple users to access. A specific “shared mailbox” recipient type was created with Exchange 2007 and continues through to Exchange 2013. A notable feature of the shared mailbox is that the associated AD account is disabled. This is unlike a traditional mailbox that was set up as an active user (with a password that had to be maintained) and then shared. Users who need to access the mailbox are granted the appropriate rights in the mailbox object, with the AD account essentially irrelevant.

Specifics

I wrote this script to create a shared mailbox for AD accounts that had been previously created by our account management team. These poor buggers use a tool written by a Unix guy in the early 00s … yes, creating accounts for the AD domain. It has awesome “features” like being able to create identical email addresses and nest groups into themselves. But I digress. We follow typical Microsoft best practice and assign access to the mailbox by way of a domain group.

The most typical use case is that specified users require full access to the mailbox, and should be able to “Send As” that mailbox. The mailbox access group is also already created by our accounts team. If it’s an adhoc request and the group does not already exist, I generally find it’s quicker to create the group in ADUC, and create the mailbox via the script.

If you received the group membership information in a nicely-formatted way (we don’t), then scripting that too would be better.

This script is not rocket science, but essentially fills in a gap and eliminates manual handling by us – it’s not much, but when you run into hundreds of account creations over the course of time, it adds up.

Assumptions:

  • AD account to be associated with the mailbox has been created, and we have its sAMAccountName
  • The AD account’s First Name, Last Name and/or Display Name fields are populated, and you have an Exchange address policy that will create addresses of the correct format for your organisation
  • A domain group has been created to be granted the Send As and Full Access rights to the mailbox, and is named with the format FASA_[MBXName]
  • User accounts that require access to the mailbox have been added to the FASA group (this can be done at any time, actually)

Of course, the biggest assumption is that most of this wouldn’t be necessary if the tool the accounts team uses worked properly!

Syntax

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

To execute the script, run the following in the Exchange PS console:

 .\bus-mbx.ps1 -mbx [MBX_NAME] -db [EX-MBX-01]

That is, run the script bus-mbx.ps1specifying the mailbox name (AD account name) and the destination Exchange database. If you only have one Exchange database, that doesn’t need to be specified (and should be removed from the script).

The script will then carry out the following tasks:

  • disable the AD account (must be done for the Shared mailbox type)
  • create the mailbox in the specified database as type “Shared”
  • grant the associated FASA group “Full access” and “Send As” permissions

Code

The script itself is very short and sweet.

param (
	[string]$mbx = $(throw "-mbx is required for mailbox name."),
	[string]$db = $(throw "-db is required for destination DB.")
)

Import-Module ActiveDirectory

# Disable existing AD account for shared MBX creation
Disable-ADAccount $mbx

# Enable shared mailbox and add SendAs/Full Access group perms
Enable-mailbox $mbx -Database $db -Shared
Add-MailboxPermission -Identity $mbx -User FASA_$mbx -AccessRights 'FullAccess'
Add-ADPermission $mbx -User FASA_$mbx -Extendedrights "Send As"
Posted in Exchange | Tagged , | 1 Comment