Validation (and DirectoryEntry) demo from TechEd 2007

I didn't get a chance to do this demo in my Exchange Automation with PowerShell session at Teched 2007, mostly due to time. However, I had a conversation with someone prior to my talk and I said that I'd cover it at some point. Well, let's cover it here in the blog and call it done. :)

The point of this blog/demo is primarily to show how Exchange 2007 object validation works, and how to use the powerful validation programmatically. A secondary benefit of this demo is that in order to "hack up" the object to get validation to fail, the most effective way is to use an LDAP DirectoryEntry object... so you get a brief view into how to do this as well. Hopefully together or separate these things are useful to you!

Ok, let's set the stage. Here's a simple mailbox object:

[PS] C:\>get-mailbox user1 | ft *quota*

ProhibitSendQuo ProhibitSendRec UseDatabaseQuot IssueWarningQuo RulesQuota
ta eiveQuota aDefaults ta
--------------- --------------- --------------- --------------- ----------
unlimited unlimited True 2GB 64KB

Next, let's set some quota values on it so we have a baseline:

[PS] C:\>set-mailbox user1 -UseDatabaseQuotaDefaults:$false

[PS] C:\>set-mailbox user1 -ProhibitSendQuota 800kb
Set-Mailbox : The value of property 'ProhibitSendQuota' must be greater than or equal to that of property 'IssueWarningQuota'. ProhibitSendQuota: '800KB', IssueWarningQuota: '2GB'.
At line:1 char:12
+ set-mailbox <<<< user1 -ProhibitSendQuota 800kb

Oh yeah, that won't work. We have logic in place to ensure that ProhibitSendQuota is always the same size or larger than IssueWarningQuota. Set-mailbox won't let us violate this rule. We still have a valid mailbox object at this point, because no ProhibitSendQuota value was written to the mailbox object (and we can easily confirm it's still valid):

[PS] C:\>(get-mailbox user1).IsValid
True

But this demo is about validation FAILING; let's hack it so it'll fail!

[PS] C:\>$a = new-object System.DirectoryServices.DirectoryEntry "LDAP://$((get-mailbox user1).DistinguishedName)"
[PS] C:\>$a.psbase.invokeset("mDBOverQuotaLimit",900*1024)
[PS] C:\>$a.psbase.CommitChanges()
[PS] C:\>Get-Mailbox user1

Name Alias ServerName ProhibitSendQuo
ta
---- ----- ---------- ---------------
user1 user1 e12 900MB
WARNING: Object e12dom.local/Users/user1 has been corrupted and it is in an inconsistent state. The following validation errors have been encountered:
WARNING: The value of property 'ProhibitSendQuota' must be greater than or equal to that of property 'IssueWarningQuota'. ProhibitSendQuota: '900MB', IssueWarningQuota: '2GB'.

Quick digression to talk about what we did here... we created a new LDAP DirectoryEntry object as $a. Then we updated one of the values (LDAP attribute mDBOverQuotaLimit) with the InvokeSet method. Finally, we saved the updated DirectoryEntry object with CommitChanges(). There are some useful references for how to use DirectoryEntry objects (including some explanation about psbase, etc) on the net. I blogged it here, pointing to BenP's post... that's probably the best place to start.

Anyway, back to the demo. We've now, evidently, succeeded in "hacking" the mailbox object into an invalid state. We got an error back telling us just what's wrong. And as a person, we can read the error, slap our forehead, and fix it up with a proper Set-mailbox operation that resets the proper quota relationship (note, you won't be able to make any other writes to this object through Set-Mailbox cmdlet until you fixup the Quota values).

But, what if you want to do it programmatically? You're in a script or whatever. Surely there has to be a way to check if an object is valid during the processing of a script? Well, of course there is. We've already used it just above... let's try it again now:

[PS] C:\>(Get-mailbox user1).IsValid
WARNING: Object e12dom.local/Users/user1 has been corrupted and it is in an inconsistent state. The following validation errors have been encountered:
WARNING: The value of property 'ProhibitSendQuota' must be greater than or equal to that of property 'IssueWarningQuota'. ProhibitSendQuota: '900MB', IssueWarningQuota: '2GB'.
False

And, since it's a boolean result, we can test it with if(), etc. Very powerful.

But we can go even one step further... what if I want to test if an object is valid before I even try to change the values? Great example of this one is now that I have an invalid object, I can't change any other properties on the object until I fix the invalid values (and make it valid again). So maybe my script should be super-smart and "test out" setting some value to see if it'll make the object valid before I try to write it back:

[PS] C:\>$b = get-mailbox user1
WARNING: Object e12dom.local/Users/user1 has been corrupted and it is in an inconsistent state. The following validation errors have been encountered:
WARNING: The value of property 'ProhibitSendQuota' must be greater than or equal to that of property 'IssueWarningQuota'. ProhibitSendQuota: '900MB', IssueWarningQuota: '2GB'.
[PS] C:\>$b.validate() | fl description

Description : The value of property 'ProhibitSendQuota' must be greater than or
equal to that of property 'IssueWarningQuota'. ProhibitSendQuota
: '900MB', IssueWarningQuota: '2GB'.

[PS] C:\>$b.ProhibitSendQuota = "unlimited"
[PS] C:\>$b.validate() | fl description <----- Note, nothing is returned here because the $b instance/object is valid now
[PS] C:\>$b.isvalid
True
[PS] C:\>set-mailbox -Instance $b
[PS] C:\>(get-mailbox user1).isvalid
True

So, you can see how this could be useful to "test the waters" in your scripts as you read/write/change objects. For instance, try the same sequence I used just above to set $b.ProhibitSendQuota back to "800kb" and you'll find it lets you do it... right up until you try to save the instance back with Set-Mailbox cmdlet. "Testing the water" with IsValid and Validate() can help you programmatically avoid this sort of error!