• 1. London, UK
  • 2. New York, NY
  • 3. Sydney, Australia
  • 4. Melbourne, Australia
  • 5. Moscow, Russia
  • 6. Singapore
  • 7. Paris, France
  • 8. Chicago, IL
  • 9. Hong Kong
  • 10. Houston, TX
Bharat Suneja

Tuesday, July 15, 2008

It's easy to get a list of all members of a Distribution Group. The Exchange shell (EMS) ships with the Get-DistributionGroupMember cmdlet that makes it a short one-liner (compared to 100s of lines of code in VBS).

However, how do we get all Distribution Groups a user, group, or contact is a member of? There's no equivalent cmdlet that can list a recipient's distribution group memberships using the shell. From the AD side, a recipient's memberOf attribute is a back-linked attribute, which I briefly talked about in memberOf Attribute can now be used in OPATH filters!. A group's membership is stored in the group's member attribute.

In the following command/script (what's the boundary between a command and a script?? when do a bunch of commands become a script?), we look at all distribution groups in AD, look at each member and determine if it matches the one we're looking for.

$contact = get-contact "[email protected]"; Get-DistributionGroup | foreach {$dg = $_ ; write-host "Looking at: "
$dg; Get-DistributionGroupMember $dg | foreach {if ($_.identity -like $contact.identity) {"Member of : " + $dg} }}

Clearly, this isn't very efficient!

Using the ADSI provider

The shell can also look at the AD objects natively using the ADSI provider. It's not as friendly or easy to use (as a native AD provider for Powershell would probably be), but it's a huge improvement over VBScript. There's no need to grab AD objects into ADO recordsets— that part is taken care of by Powershell.

Here's one way to do this using the ADSI provider:

$dn = "LDAP://" + (Get-Contact [email protected]).distinguishedName; $foo=[ADSI]$dn; $foo.memberOf | foreach {$dg = $_; get-distributiongroup $dg}

Here's a script with some changes and validation: Get-DGMembership.zip

What it does: Uses the ADSI provider to get list of all groups a recipient is a member of, determines if the group is a Distribution or Security group, outputs names of Distribution Groups.

.\Get-DGMembership.ps1 [email protected]

.\Get-DGMembership.ps1 [email protected] [email protected]

What we can really use is a native AD provider that lends the same automation capabilities to AD management tasks that the Exchange shell and Powershell lend to Exchange and Windows management tasks.

Labels: , , , , ,

Friday, June 15, 2007

In Exchange Server 2003, Exchange System Manager's Logons view for a Mailbox Store (expand Storage Group | Mailbox Store | Logons) displays client logon-related details.

The default view displays 1. User name 2. Windows 2000 Account 3. Logon Time 4. Last Access Time 5. Client Version. It does not display the client IP address from which users are logged on.

To display the IP address, add the Client IP Address column:

1. Expand the mailbox store | right-click Logons | View | Add/Remove Columns

2. Add Client IP Address

Besides the Client IP Address and other defaults mentioned above, the following logon-related fields are available:
1. Adapter Speed
2. Client Mode (Cached or not)
3. Client Name
4. Code Page
5. Folder Ops
6. Full Mailbox Directory Name
7. Full User Directory Name
8. Host Address
9. Latency
10. Locale ID
11. MAC Address
12. Messaging Ops
13. Open Attachments
14. Open Folders
15. Open Messages
16. Other Ops
17. Progress Ops
18. RPC Calls Succeeded
19. Stream Ops
20. Table Ops
21. Total Ops
22. Transfer Ops

For the scripting geeks amongst us, these properties are exposed by the Exchange_Logon WMI class.

Labels: , ,

Sunday, December 17, 2006


Conflicting Mailbox Store Policies

Posted by Bharat Suneja at 10:19 PM
I spent some time (ok, I'll admit - more than "some time"... ) writing a script to get user mailbox storage limits/quotas [an improvement on the script I posted earlier - read previous post "SCRIPT: Show mailbox limits"]. The new script checks users' individual mailbox limits (if these are set in user properties in ADUC). If no individual limits exist, it checks the limits on user's mailbox Store, and also checks if any System Policies apply mailbox storage limits to the Store. If any policies are found, it checks the limits on those as well, and reports on what the user's actual limits are. Something like the Resultant Set of Policies feature/snap-in for Group Policies, for user's mailbox storage quotas.

Out of curiosity (and in an attempt to see if the script breaks), I tried to apply multiple Mailbox Store policies that apply storage limits to the same Store. Almost certain that Exchange System Manager will do no validation if an existing Mailbox Policy is already applied, I was pleasantly surprised to find that it in fact does validate!

When trying to apply a second Mailbox Store policy (with storage limits) to a Mailbox Store that already has such a policy applied, ESM throws up the following error:

At this point, the easy choice is to click on the "Yes" button, but I was still hoping if I selected No the second conflicting policy may be applied :)

No such luck - ESM curtly informs you that the second policy could not be applied "... because you refused to remove the object from the control of conflicting policies."

I can't help but think about all the thought that goes into creating such software - though we all have our own pet peeves and can count the things we don't like about Exchange, for a change it's great to be able to appreciate the things you do like about it. Having worked on a couple of cool software features in Zenprise (stuff I'd love to talk and blog about soon!), I have a new-found appreciation for such thoughtfulness and attention to detail.

This certainly helps me avoid writing another few hundred lines of code to figure out if multiple System Policies for mailbox quotas are applied to a Store, which one has priority and how to make the script figure that out.

As a sidenote, writing a script using VBScript to accomplish something in an Exchange Server 2003/2000 environment seems like a real pain now - given what I accomplished in about 300 lines of code tonight is so easily accomplished using a one-liner in Exchange (2007) shell, and something I do several times a day! (At times just because it's so easy to do, without writing any code! :)

The Exchange shell equivalents:
Get-mailbox | select name,database,*quota*

Labels: , ,

Tuesday, September 05, 2006


Using Consolas as the Windows console font

Posted by Bharat Suneja at 8:00 AM
If you don't like the font choices (or lack thereof) in Windows console, or want Consolas (a new font available by installing IE7 or Windows Vista) as your console font, you can add it as an option by going to HKLM\Software\Microsoft\WindowsNT\ CurrentVersion\TrueTypeFont.

Scott Hanselman shows you how in this blog post.

Question for the Windows Vista folks - why not make it available as a choice by default? As the name suggests, it's an ideal fit for the console/text-based apps.

Labels: , ,

Saturday, February 05, 2005

The ClusApi.h file, which is part of the PlatformSDK should contain this...

ClusterResourceStateUnknown = -1,
ClusterResourceInherited = 0,
ClusterResourceInitializing = 1,
ClusterResourceOnline = 2,
ClusterResourceOffline = 3,
ClusterResourceFailed = 4,
ClusterResourcePending = 128,
ClusterResourceOnlinePending = 129,
ClusterResourceOfflinePending = 130

Alain is (now) WMI Program Manager at Microsoft and author of :
1. Understanding Windows Management Instrumentation (WMI) Scripting and
2. Leveraging Windows Management Instrumentation (WMI) Scripting - 2 of the best references on WMI. He also writes for Windows Scripting Solutions newsletter (published by Penton Media, the publishers of Windows IT Pro & SQL Server magazines).

Labels: , ,

Friday, January 28, 2005


Scripting: ExchangeClusterResource class

Posted by Bharat Suneja at 1:40 PM
The WMI ExchangeClusterResource class has 5 properties:
1) Name: returns name of Exchange cluster resource
2) Type: specifies the cluster resource type (IP Address, network name, etc.)
3) Owner: specifies the cluster node that the resource is running on (changes with failover)
4) VirtualMachine: returns name of the Virtual Machine that owns the resource
5) State: Shows the current state of resource, returns a numerical value.

The value I get for all online resources is 2 - but I haven't been able to find any cross-reference that translates the numbers into something, except for the Windows Clustering section of the Platform SDK that mentions the following values (not-numerical) for the GetClusterResourceState function.

Return Code Description
ClusterResourceInitializingThe resource is performing initialization.
ClusterResourceOnlineThe resource is operational and functioning normally.
ClusterResourceOfflineThe resource is not operational.
ClusterResourceFailedThe resource has failed.
ClusterResourcePendingThe resource is in the process of coming online or going offline.
ClusterResourceOnlinePendingThe resource is in the process of coming online.
ClusterResourceOfflinePendingThe resource is in the process of going offline.
ClusterResourceStateUnknownThe operation was not successful. For more information about the error, call the Win32 function GetLastError.

Assuming these values map serially to the numerical values returned by the WMI class, 2 would mean the resource is operational and functioning normally.

However, when moving the MSDTC resource, the numerical value returned while the resources were moving was 129. That blows holes in the theory.

Labels: , ,

Friday, November 05, 2004


HTML: Add a BACK text link (Javascript)

Posted by Bharat Suneja at 3:02 PM
To add a "Back" text link to a web page:
<a href="JavaScript:history.back(1)">the past</a>

Using VBScript and FORM button:
<FORM method="POST" name="VBScript_Nav_Sample">

<INPUT TYPE="button" NAME="Go Back!" VALUE="VBScript_Backup_Sample" LANGUAGE="VBScript" OnClick="call window.history.back(1)">


Using Javacript and FORM button:

<input type="button" value="Button" name="B3" onClick="javascript:history.back(1)">


From Microsoft KB Q205693

Labels: ,

Monday, September 20, 2004


VBScript | SELECT CASE statement

Posted by Bharat Suneja at 3:06 PM
Rather than using too many IF-THEN conditions to check a value and take action, you are better off using the SELECT CASE condition. It produces cleaner code and is specifically meant for multiple IF-THEN conditions.

The correct use of SELECT CASE:

Select Case strValue [strValue is the variable you want to evaluate]
Case "John"...
do someting here if strValue = "John"
Case "Paul"...
do someting here if strValue = "Paul"
Case Else... [Unlike IF-THEN-ELSE, Select Case has CASE ELSE as the "catch-all"]
do this if strValue does not = "John" or "Paul"
End Select [END SELECT ends the Select Case loop, like END IF for IF-THEN]


Wednesday, September 08, 2004


Web-enabling ADSI scripts

Posted by Bharat Suneja at 6:36 PM
With the number of scripts I've been writing/accumulating growing exponentially every week, it was time to web-enable stuff so others could also benefit from the automation and efficiencies these provide.

Well, it proved to be more difficult than I thought. Finally, after 2-3 sleepless nights, some things are up and running on a small web site, with more stuff to be added as I go. Used to be a big design evangelist - still am - but at times all the design stuff just gets in the way of coding! (For someone with a background in advertising, design & all things creative I'm really surprising myself with this line of thought!).

There will be time for design later - for now it's just fun (and very rewarding) to make the functionality work!

Some lessons learnt:
i) Security: You don't want to run this stuff on a Domain Controller. Here's where you run into some interesting issues - that perhaps took most of the time. If the web site resides on a Member Server, IIS' Windows Integrated Authentication does not work for talking to the domain! Choose Basic authentication - and use SSL.

**You may have to tell IIS to explicitly use Kerberos.

cscript C:\Inetpub\AdminScripts\adsutil.vbs set w3svc/NTAuthenticationProviders "Negotiate"

ii) HTML/ASP Forms:Been a long while since I dabbled with Active Server Pages (ASP). This was a good refresher.

Forms have 2 methods - GET & POST. The GET method exposes data in the URLs like this URL -
POST does not.

Form Page: first tags are in quotes to avoid this blog interpreting it as an actual form.

< "FORM" name="User" ACTION="getuserdn.asp" method="post">
Enter User Name <"input maxlength"="20" size="30" name="inputUser">

inputUser is the name of the text box, this gets passed on to the next page (ACTION) as a variable

<"input type"="submit" value="Get User Details" name="Get_DN">

Result Page

< %@ Language=VBScript%>
< %strUser = request.Form("inputUser")
I prefer to get the form input (inputUser) in a new variable - strUser here - for no explicit reason.
'Then script stuff as usual
% >

The above code captures the value of the inputUser input box on the previous (form) page.