Set-CalendarProcessing -ResourceDelegates (and Friends) Without Tears

(Get-CalendarProcessing -Identity “MyAwesomeRoom”).ResourceDelegates is equal to (Get-Mailbox -Identity “MyAwesomeRoom”).GrantSendOnBehalfTo. This is good to know, because while you can do Set-Mailbox -GrantSendOnBehalfTo @{add=”user3@awesome.com”} and not kick out user1 and user2, Set-CalendarProcessing does not do this. If you try it, you get back this mess:

Cannot process argument transformation on parameter ‘ResourceDelegates’. Cannot convert the “System.Collections.Hashtable”
value of type “System.Collections.Hashtable” to type “Microsoft.Exchange.Configuration.Tasks.RecipientIdParameter[]”.

Unfortunately, I have yet to find a similar, more friendly route for adding BookInPolicy, RequestInPolicy or RequestOutOfPolicy.

All of these parameters will take an array, so you need to read in what’s there, append your new delegate(s), then write the new list to Set-CalendarProcessing:

function Add-CalendarResourceDelegate {
Param(
$roomName
, $newDelegate
)
$resourceDelegates = (Get-CalendarProcessing -Identity $roomName).ResourceDelegates
$resourceDelegates += $newDelegate
Set-CalendarProcessing -Identity $roomName -ResourceDelegates $resourceDelegates
}
function Add-CalendarBookInPolicy {
Param(
$roomName
, $newDelegate
)
$bookInPolicy = (Get-CalendarProcessing -Identity $roomName).BookInPolicy
$bookInPolicy += $newDelegate
Set-CalendarProcessing -Identity $roomName -BookInPolicy $bookInPolicy
}

Add-CalendarResourceDelegate -roomName “MyAwesomeRoom” -newDelegate user3

Removing delegates is a bit trickier; they are stored in the array as canonical names (awesome.com/Site1/Users/user1), not email or UserPrincipalName (User.One@awesome.com or user1@awesome.com). However, you just need to do (Get-Mailbox user1).Identity to get this.

function Remove-CalendarResourceDelegate {
Param(
$roomName
, $delegateToRemove
)
$resourceDelegates = (Get-CalendarProcessing -Identity $roomName).ResourceDelegates
$delegateToRemoveIdentity = (Get-Mailbox $delegateToRemove).Identity
$resourceDelegates.Remove($delegateToRemoveIdentity)
Set-CalendarProcessing -Identity $roomName -ResourceDelegates $resourceDelegates
}

And so on for BookInPolicy, RequestInPolicy and RequestOutOfPolicy.

A step closer to extending ActiveRoles Server to handle all the Set-CalendarProcessing attributes…

The publicDelegates AD Attribute: Change GrantSendOnBehalfTo the ARS Way

While the Active Directory object’s publicDelegates attribute matched the contents of (Get-Mailbox “aliasGoesHere”).GrantSendOnBehalfTo for all the resource mailboxes and distribution groups I checked, I hesitated to give the way of modifying publicDelegates directly with ARS instead of using Set-Mailbox -GrantSendOnBehalfTo with Exchange Management Shell in my previous post on this topic. A little more research (and trial and error) shows that indeed, publicDelegates really is what Exchange uses to decide who gets to send stuff on behalf of another mailbox.

Here is a thread from the [ActiveDir] listserv archive in which Joe Richards, an author of the classic O’Reilly Active Directory reference, states that “[t]he stuff in AD is strictly how Send On Behalf is controlled” and goes on to explain how an update bug in Exchange 2003 might prevent a recent change from being propogated to a user in a different domain from the delegated object. Since the conversation took place before Exchange 2007 was on the scene, I was a bit skeptical about its applicability to Exchange 2010.

To see which one of several dozen edsva-MsExch-blahblahblah attributes does this for the ARS connector to Exchange 2010, I used the ARS web interface to add and remove delegates from the “Send on Behalf” list of one of the test room mailboxes (Exchange Properties -> Delivery Options -> (Send on behalf) “Add”), then looked at the Change History for that room mailbox. The publicDelegates attribute was the only item changed. Still, I was concerned: the publicDelegatesBL for the delegates outside the domain of the room mailbox weren’t getting updated.

For comparison, I added some delegates the Exchange way:

Set-Mailbox “Room Name” -GrandSendOnBehalfTo @{add=”username”}

Then the Quest AD Management Shell way:

Set-QADUser -ObjectAttributes @{publicDelegates=@{append=(Get-QADUser username).DN}}

(trying to append the username directly didn’t work; it wants the distinguishedName)

After some more experimentation, I discovered that when a user in an different domain is added to the publicDelegates attribute, the delegated object will not show up in the user’s publicDelegatesBL, whether you use Exchange, Quest AD Management Shell or the ARS web interface, while the change to publicDelegatesBL occurs immediately for delegates within the domain of the delegated object. Cross-domain delegated objects will only show up in a user’s publicDelegatesBL once that user has connected to the object for the first time.

If anyone knows why this is, what trouble it might cause, or better yet, how to fix it, please post a comment. More importantly, if anyone knows of a reason NOT to use publicDelegates instead of GrandSendOnBehalfTo, please let me know. And let the ActiveRoles Server dev team know 🙂

Fun fact about the CalendarProcessing ResourceDelegates attribute: it is equal to a resource mailbox’s GrantSendOnBehalfTo, which is controlled by the AD attribute publicDelegates.

Next step: figuring out how to control the other CalendarProcessing attributes via ARS.

Goal: Complete management of resource mailboxes via ARS.

Modifying GrantSendOnBehalfTo Without Tears

If you run the cmdlet

Set-DistributionGroup "Sales Team" -GrantSendOnBehalfTo jonestom

you will overwrite the current contents of that distribution group’s GrantSendOnBehalfTo list. Oops.

One of my colleagues, a very old Exchange hand and a new PowerSheller, asked me if there was a simpler way to add one mailbox owner ($trustee) to the GrantSendOnBehalfTo attribute of another Exchange mailbox or distribution group ($target) than having to get both items, extract $trustee.distinguishedname and the current value of $target.GrantSendOnBehalfTo (and save it to another variable, $currentTrustees), append the trustee’s distinguished name to the current list of trustees ($currentTrustees += $trustee.DistinguishedName), then finally replace the old value of $target.GrantSendOnBehalfTo with $currentTrustees (Set-DistributionGroup $target -GrantSendOnBehalfTo $currentTrustees)

Yes, there is, at least as of Exchange 2010 SP1.

Set-DistributionGroup "Sales Team" -GrantSendOnBehalfTo @{add='domainA\jonestom','domainB\smithjane'}

Removing an unwanted trustee works the same way:

Set-DistributionGroup "Sales Team" -GrantSendOnBehalfTo @{remove='domainA\jonestom'}

Because combining these operations often does not end well, here is a cmdlet I made for the help desk to replace a departing colleague with his successor:

function Replace-DLGrantSendOnBehalfTo {
Param($oldTrustee,
	$newTrustee,
	$targetDistributionGroup
	)
Set-DistributionGroup $targetDistributionGroup -GrantSendOnBehalfTo @{add=$newTrustee}
Set-DistributionGroup $targetDistributionGroup -GrantSendOnBehalfTo @{remove=$oldTrustee}
}

For greater efficiency, it can be part of a pipeline, preceded by the names of the target distibution groups and surrounded by a foreach statement. A similar cmdlet can be made for mailboxes. There is probably a way to make it more generic; I might add that later.

The only article I’Ve seen that showed the @{add=$foo}, @{remove=$bar} way to modify this: http://blogs.technet.com/b/manjubn/archive/2012/10/02/grantsendonbehalfto-permissions-for-mailbox-overwrites-existing-permission.aspx

An article that shows the longer way around I described at the beginning, but gives a good explanation of the difference between GrantSendOnBehalfTo and SendAs permissions in Exchange: http://exchangeserverpro.com/exchange-2010-grant-send-behalf-permissions-distribution-group

Finding disconnected mailboxes the PowerShell and EMS way

Like most IT books, Microsoft Exchange Server 2010 Inside Out is a huge doorstop of a book. Since my boss was kind enough to purchase it, and I’m by far the department’s foremost reader of English and its least-experienced Exchange admin, I’ve decided to try to read the whole thing, a chapter a day.

It’s already paid off – I solved a problem using it just a day after reading the fantastic “Exchange Management Shell” chapter.

When a user gets transferred to a location in another domain in our Active Directory forest, he gets a new user account in that domain, but his old mailbox is moved to the Exchange server for that location in the new domain. Occasionally, something goes wrong and the mailbox ends up in a disconnected state – in this case, the old account was deleted before the mailbox was connected to the new one. The real problem was that the help desk staff member who did the move couldn’t remember which mailbox database (or even server) he moved the mailbox to…

Exchange Management Shell and Tony Redmond to the rescue!

The script below is adapted from “Exchange Server 2010 Inside Out”, page 132, mostly to overcome the pipelining problem Tony describes in the “Code changes required by remote PowerShell” section on page 120. I ran this, as I run everything I can, in an implicit Exchange remoting session in my trusty PowerGUI, rather than in Exchange Management Shell itself. Make sure you are either implicitly remoted to an Exchange server or in Exchange Management Shell before running it.

$mbxServers = (Get-MailboxServer | ForEach-Object { $_.name })
Foreach ($mbxserver in $mbxServers) {Get-MailboxStatistics -Server $mbxserver `
| where {$_.disconnectdate -ne $null } | select displayname,OriginatingServer, `
DisconnectDate,LastLoggedOnUserAccount,LastLogonTime,TotalItemSize,Database `
| Export-Csv c:\temp\$mbxserver-disconnectedmailboxes.csv -NoTypeInformation }

The main weakness is that it will only find disconnected mailboxes on 2007 or 2010 servers, but that’s because Get-MailboxStatistics only works on those newer mailboxes.

Tony Redmond’s blog: http://thoughtsofanidlemind.wordpress.com/

Getting ints out of Exchange 2010 disk space statistics Strings when remoting

July 2015 update: This still applies to Exchange 2013 – I’ve not tested it yet with Exchange 2016, though…

I’m back, this time with Exchange stuff. It’s been awhile, mostly due to my company’s IT department having found out what happens when DNS and AD do not get along and everything starts falling back to NTLM authentication, as well as a long Thanksgiving vacation back to the States. However, I’ve begun a project to overhaul our old user provisioning process, and ActiveRoles Server is at the center of it.

User provisioning means email provisioning, and in an Exchange environment, that means Exchange cmdlets.

The PowerShell cmdlets for Exchange 2010 are about 90% awesome. Why not 100%? Little stuff like returning disk space statistics from Get-MailboxDatabase -Status or Get-MailboxStatistics as strings when doing implicit remoting, not the ByteQuantifiedSize type returned when you use Exchange Management Shell on the server itself, or as a simple int.

This is because ByteQuantifiedSize is not a regular .NET type, but a Microsoft.Exchange.Data type, so instead of rendering it as a plain-vanilla int when the Microsoft.Exchange.Management.PowerShell.E2010 snap-in isn’t loaded, PowerShell renders it as a String. Microsoft wants us to use implicit remoting instead of the snap-in when running remotely. Make up your minds, Exchange team!

Technet forum discussion about this inconsistency: Exchange Server 2010: why string here and int there with EMC?

Example:

Get-MailboxDatabase -Server US1234 -Status `
| Select-Object AvailableNewMailboxSpace `
| Sort-Object -Descending


AvailableNewMailboxSpace
------------------------
94.88 MB (99,483,648 bytes)
1.58 GB (1,696,464,896 bytes)

Wait… 1.58 GB is bigger than 94.88 MB, right? Not if they’re strings.

However, this can be overcome. I’m posting this because all the ways I could find are stuck deep inside rather long scripts. This method is from http://poshcode.org/1902, by Karl Mitschke (see line 120). I chose to leave it as bytes rather than converting to MB or GB, as the destination for this data is a SQL Server table that will immediately be used for the user provisioning project, but might be used for other things in the future.

Our mailbox databases are named for the sites they’re at: for example, one of the ones at the US-Dallas site would be US-Dallas-MD01. The sitename can be extracted with Split(‘-‘).

Get-MailboxDatabase -Status `
| Select-Object @{Name="Sitename"; `
Expression={$_.Name.split('-')[0]+'-'+$_.Name.split('-')[1]}}, `
Name, Server, `
@{Name="Available"; `
Expression={[int]($_.availablenewmailboxspace.split("(")[1].Split()[0])}} `
| Sort-Object -Property @{Expression="Sitename";Descending=$false}, `
@{Expression="Available";Descending=$true}

That finally got me what I was looking for.

Here’s a function for converting those disk space strings to integers – I’ve added it to the script I use to start Exchange implicit remoting sessions from my workstation.

Function Get-IntFromExchangeNumberString {
Param($NumberString)
 [int]($NumberString.split("(")[1].Split()[0])
}

If there is a better and/or more concise way to do any of what I’ve done, feel free to put it in the comments or post a link!