If you missed the other articles in this series please read:
- Scripting Exchange Using VBScript and ADSI (Part 1)
- Scripting Exchange Using VBScript and ADSI (Part 2)
Exchange Objects Creation and Manipulation in Active Directory using Scripting
Accessing Exchange objects is done using a mixture of ADSI and CDOEXM discussed in my previous articles. You can also access some Exchange information using WMI, a scripting interface that can be used to query both hardware and software so you can query an Exchange server, its services and, say, the CPU on that machine at the same time.
Moving Mailboxes
CDOEXM has corresponding objects to the main Exchange objects – Servers, Storage Groups, Mailbox stores, and databases. The “CreateObject” function is used to declare these objects though the objects already exist and the objects are accesses and not created in the script.
Function MoveMailbox (Server, RecipName, Moveto_MailboxStore, Moveto_StorageGroup, Moveto_Server)
‘ Declare variables.
Dim objUser ‘ IADsUser
Dim iAdRootDSE ‘ ActiveDs.IADs
Dim objMailbox ‘ CDOEXM.IMailboxStore
Dim objServer ‘ CDOEXM.ExchangeServer
Dim objSG ‘ CDOEXM.StorageGroup
Dim objMSDB ‘ CDOEXM.MailboxStoreDBDim iDS ‘ IDataSource2
The first declarations are pretty much self-explanatory as we declare objects for a user, Active Directory root, and the Exchange objects. You might wonder what is “IDataSource2” and where did the first “IDataSource” go.
“IDataSource2” is an Interface. An interface is a way of looking at an Exchange object. Each interface has its own methods. The “IDataSource2” interface is used for deleting Exchange objects. While there are other ways to delete Exchange objects (for example, using ADSI) this method also performs numerous Exchange related checks and cleanup procedures necessary for proper store maintenance.
Dim storegroup ‘ Variant
Dim mbx ‘ Variant
Dim bFound ‘ Boolean
Dim sMailStorePath ‘ String
Dim sDirectoryServer ‘ String
Dim sDomainName ‘ String‘ Initialize MoveMailbox
MoveMailbox = FalseSet objServer = CreateObject(“CDOEXM.ExchangeServer”)
Set objSG = CreateObject(“CDOEXM.StorageGroup”)
Set objMSDB = CreateObject(“CDOEXM.MailboxStoreDB”)‘ Initialize bFound.
bFound = False‘ Note that although the object is formally known as “IDataSource2”
‘ it is accessed as “IDataSource”
Set iDS = objServer.GetInterface(“IDataSource”)
iDS.Open Moveto_Server‘ Check that the destination mailbox store exists.
For Each storegroup In objServer.StorageGroups
objSG.DataSource.Open storegroup
If UCase(Moveto_StorageGroup) = UCase(objSG.Name) Then
For Each mbx In objSG.MailboxStoreDBs
objMSDB.DataSource.Open mbx
If UCase(Moveto_MailboxStore) = UCase(objMSDB.Name) Then
bFound = True
sMailStorePath = mbx
Exit For
End If
Next
End If
If bFound Then Exit For
NextIf Not bFound Then
wscript.echo “The destination MailStore is not found.”
Exit Function
End If‘ Get the default naming context.
Set iAdRootDSE = GetObject(LDAP://RootDSE)
sDomainName = iAdRootDSE.Get(“defaultNamingContext”)
wscript.echo sDomainName‘ Get the Active Directory user object. This sample script assumes that the user exists
‘ in the default container but you may rewrite to search for the user as shown in the
‘ previous article.Set objUser = GetObject(“LDAP://CN=” + RecipName + “,CN=users,” + sDomainName)
Set objMailbox = objUser‘ Check if a mailbox exists for the user.
If objMailbox.HomeMDB = “” Then
wscript.echo “There is no mailbox to move.”
Else
wscript.echo “Current MDB: ” + objMailbox.HomeMDB
wscript.echo “Moving mailbox…”‘ Move the mailbox.
objMailbox.MoveMailbox “LDAP://” + sMailStorePath‘ Save data into the data source.
objUser.SetInfowscript.echo “The mailbox has been moved to ‘” + Moveto_MailboxStore + _
“‘ mailbox store successfully.”
MoveMailbox = True
End If‘ Verify that the mailbox has been moved, verify that the HomeMDB server
‘ now matches Moveto_Server.
Set objUser = GetObject(“LDAP://CN=” + RecipName + “,CN=users,” + sDomainName)
Set objMailbox = objUser
If UCase(objMailbox.HomeMDB) = UCase(sMailStorePath) Then
wscript.echo “The mailbox move is verified.”
Else
wscript.echo “The mailbox move is not verified.”
End If
End Function
Using the Move Mailbox Script
So, you must be thinking, how am I going to use this script? After all, you can move users from one Exchange database to another using Active Directory Users and Computers. You can also use the Find dialog box to group users with similar properties. Of course, scripting can automate this process and you can, for example schedule such an operation, but, just to prove the point, how can you move users from an OU branch? Sure, you can select all the users in a single OU, right click them an use the “Exchange Tasks” option, but this will not move users in nested OUs. So, if you want to move an entire branch of a company that has a complex OU design (this is sometimes required for Group Policy purposes) it might prove to be a slow manual process.
This is easily done using scripting. Recursion can be used to find all the Exchange users in an OU tree and move all of them.
Sub MoveOUMailboxes (Server, OU, Moveto_MailboxStore, Moveto_StorageGroup, Moveto_Server)
‘ Declare variables.
Dim objUser ‘ IADsUser
Dim iAdRootDSE ‘ ActiveDs.IADs
Dim objMailbox ‘ CDOEXM.IMailboxStore
Dim objServer ‘ CDOEXM.ExchangeServer
Dim objSG ‘ CDOEXM.StorageGroup
Dim objMSDB ‘ CDOEXM.MailboxStoreDBDim iDS ‘ IDataSource2
Dim storegroup ‘ Variant
Dim mbx ‘ Variant
Dim bFound ‘ Boolean
Dim sMailStorePath ‘ String
Dim sDirectoryServer ‘ String
Dim sDomainName ‘ String‘ Initialize MoveMailbox
MoveMailbox = FalseSet objServer = CreateObject(“CDOEXM.ExchangeServer”)
Set objSG = CreateObject(“CDOEXM.StorageGroup”)
Set objMSDB = CreateObject(“CDOEXM.MailboxStoreDB”)‘ Initialize bFound.
bFound = False‘ Note that although the object is formally known as “IDataSource2”
‘ it is accessed as “IDataSource”
Set iDS = objServer.GetInterface(“IDataSource”)
iDS.Open Moveto_Server‘ Check that the destination mailbox store exists.
For Each storegroup In objServer.StorageGroups
objSG.DataSource.Open storegroup
If UCase(Moveto_StorageGroup) = UCase(objSG.Name) Then
For Each mbx In objSG.MailboxStoreDBs
objMSDB.DataSource.Open mbx
If UCase(Moveto_MailboxStore) = UCase(objMSDB.Name) Then
bFound = True
sMailStorePath = CStr(mbx)
Exit For
End If
Next
End If
If bFound Then Exit For
NextIf Not bFound Then
wScript.echo “The destination MailStore is not found.”
Exit Sub
End If‘ Get the default naming context.
Set iAdRootDSE = GetObject(“LDAP://RootDSE”)
sDomainName = iAdRootDSE.Get(“defaultNamingContext”)‘ Get the Active Directory Base OU
Set ObjOU = GetObject (“LDAP://OU=” & OU & “,” & sDomainName)
MoveOUUsers ObjOU, sMailStorePathEnd Sub
Sub MoveOUUsers (oObject,Path)
Dim objUser, objContainerFor Each objUser in oObject
‘ Get all the users within the OU
if objUser.Class=”user” and objUser.msExchHomeServerName <> “” then
Set objMailbox = objUser
‘ Move the mailbox
WScript.Echo “Moving…” & objUser.displayName
objMailbox.MoveMailbox “LDAP://” + Path
‘ Save data into the data source.
objUser.SetInfo
WScript.Echo “Done.”
End if
Next
For Each oContainer in oObject
‘ Get all the OUs within this OU and do them as well
If oContainer.Class=”organizationalUnit” or oContainer.Class=”container” then
If UsersinOU (oContainer) then MoveOUUsers oContainer,Path
End if
Next
End SubFunction UsersinOU (oObject)
Dim oUser
UsersinOU = false
for Each oUser in oObject
if oUser.Class = “user” and oUser.msExchHomeServerName <> “” then UsersinOU = true
Next
End Function
Getting Mailboxes Size
At this time (and this might change in the next Exchange version, E12), no single programming interface will cover Exchange. CDO is used to access Outlook items and can be used to get the mailbox size using MAPI, but it requires the user running the script to have access to all the mailboxes, a right denied by default in Exchange 2000/3 to Exchange administrators. However, this is the only method for Exchange 2000 servers. With Exchange 2003 server you can now retrieve the size of a mailbox using WMI. WMI you might ask? What? Now I have to learn another interface?
Microsoft developed WMI as a derivative of SNMP, a popular protocol that allows a single interface to monitor networking equipment. Any device that can communicate over the network and supports this protocol can provide information on network activity, CPU, memory and such. Microsoft extended this model to Windows applications and provided methods for not only retrieving information but also running application specific functions just like other programming interfaces can.
WMI has become quite popular with Microsoft, so almost every service pack and new version of any Microsoft server products expands the WMI interfaces available. However, new WMI interface are not added to old products, so you have WMI interfaces that exist for Exchange 2003 but not for Exchange 2000. One of these interfaces is the one that allows you to retrieve the size of a mailbox.
To access an application using WMI you require a CIM namespace, which defines the information available through the application.
On Error Resume Next
Dim cComputerName
Const cWMINameSpace = “root/MicrosoftExchangeV2”
The rest of the script will fetch mailbox sizes for our server. Mailbox sizes are in Kilobytes.
Const cWMIInstance = “Exchange_Mailbox”
cComputerName = “EX2003” ‘ Modify this value to suit your serverDim strWinMgmts ‘ Connection string for WMI
Dim objWMIExchange ‘ Exchange Namespace WMI object
Dim listExchange_Mailboxs ‘ ExchangeLogons collection
Dim objExchange_Mailbox ‘ A single ExchangeLogon WMI object‘ Create the object string, indicating WMI (winmgmts), using the
‘ current user credentials (impersonationLevel=impersonate),
‘ on the computer specified in the constant cComputerName, and
‘ using the CIM namespace for the Exchange provider.
strWinMgmts = “winmgmts:{impersonationLevel=impersonate}!//”& _
cComputerName&”/”&cWMINameSpace
Set objWMIExchange = GetObject(strWinMgmts)
‘ Verify we were able to correctly set the object.
If Err.Number <> 0 Then
WScript.Echo “ERROR: Unable to connect to the WMI namespace.”
Else
‘
‘ The Resources that currently exist appear as a list of
‘ Exchange_Mailbox instances in the Exchange namespace.
Set listExchange_Mailboxs = objWMIExchange.InstancesOf(cWMIInstance)
‘
‘ Were any Exchange_Mailbox Instances returned?
If (listExchange_Mailboxs.count > 0) Then
‘ If yes, do the following:
‘ Iterate through the list of Exchange_Mailbox objects.
For Each objExchange_Mailbox in listExchange_Mailboxs
Wscript.Echo””
‘
‘ Display the value of the Size property.
WScript.echo objExchange_Mailbox.MailboxDisplayName & “,” & objExchange_Mailbox.Size
‘
Next
Else
‘ If no Exchange_Mailbox instances were returned,
‘ display that.
WScript.Echo “WARNING: No Exchange_Mailbox instances were returned.”
End If
End If
WMI can seem scary at first with its complex notation but you can always copy and paste existing scripts and modify them to suit your needs.
Storage Group Dilemma
However, WMI does not provide an interface for accessing storage groups or individual databases yet. This means that you cannot query a mailbox store for its mailboxes. So, what do you do if you want to create a list of mailboxes on a single mailbox store?
For now you will just have to filter the results of the previous scripts by implementing an LDAP query to Active Directory to find out whether the relevant user’s mailbox resides on the relevant mailbox store.
On Error Resume Next
Dim cComputerName
Const cWMINameSpace = “root/MicrosoftExchangeV2”
Const cWMIInstance = “Exchange_Mailbox”
cComputerName = “EX2003”Dim strWinMgmts ‘ Connection string for WMI
Dim objWMIExchange ‘ Exchange Namespace WMI object
Dim listExchange_Mailboxs ‘ ExchangeLogons collection
Dim objExchange_Mailbox ‘ A single ExchangeLogon WMI objectSet rootDSE = GetObject(“LDAP://RootDSE”)
DomainContainer = rootDSE.Get(“defaultNamingContext”)strWinMgmts = “winmgmts:{impersonationLevel=impersonate}!//”& _
cComputerName&”/”&cWMINameSpace
Set objWMIExchange = GetObject(strWinMgmts)
‘ Verify we were able to correctly set the object.
If Err.Number <> 0 Then
WScript.Echo “ERROR: Unable to connect to the WMI namespace.”
ElseSet listExchange_Mailboxs = objWMIExchange.InstancesOf(cWMIInstance)
If (listExchange_Mailboxs.count > 0) Then
For Each objExchange_Mailbox in listExchange_Mailboxs
If IsUserinDB (objExchange_Mailbox.MailboxDisplayName)= True Then _
WScript.echo objExchange_Mailbox.MailboxDisplayName & “,” & objExchange_Mailbox.SizeNext
ElseWScript.Echo “WARNING: No Exchange_Mailbox instances were returned.”
End If
End IfFunction IsUserinDB (strDisplayName)
IsUserinDB = False
Set conn = CreateObject(“ADODB.Connection”)
conn.Provider = “ADSDSOObject”
conn.Open “ADs Provider”‘ The LDAP String is a query that checks whether there is a user with the on the DB1 mailbox store
‘ Of the SG2 Storage Group and the rest of the information belonging to my test server.
‘ You should change this to match the information of your environment.ldapStr = “<LDAP://” & DomainContainer & _
“>;(&(&(&(& (mailnickname=*) (| (&(objectCategory=person)(objectClass=user)(homeMDB=CN=DB1,CN=SG2,CN=InformationStore,CN=EX2003,CN=Servers,CN=First Administrative Group,CN=Administrative Groups,CN=Sunnydale,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=sunnydale,DC=muni)) )))(objectCategory=user)(displayName=” & _
StrDisplayName & “)));adspath;subtree”Set rs = conn.Execute(ldapStr)
if rs.RecordCount = 1 then IsUserinDB = Trueconn.Close
End function
Moving Mailbox by Size
So, now that we’ve mastered the means of obtaining a mailbox’s size, we can combine all the knowledge acquired here to, for example, move large mailboxes to a different storage group. To do this the following script will execute a query for all Exchange users using an LDAP query, find using WMI the mailbox size and move using CDOEX the mailbox to a new storage group.
Sub MoveBigMailboxes (Server, Moveto_MailboxStore, Moveto_StorageGroup, Moveto_Server,Oversize)
Const cWMINameSpace = “root/MicrosoftExchangeV2”
Const cWMIInstance = “Exchange_Mailbox”
‘ Declare variables.
Dim objUser ‘ IADsUser
Dim iAdRootDSE ‘ ActiveDs.IADs
Dim objMailbox ‘ CDOEXM.IMailboxStore
Dim objServer ‘ CDOEXM.ExchangeServer
Dim objSG ‘ CDOEXM.StorageGroup
Dim objMSDB ‘ CDOEXM.MailboxStoreDBDim iDS ‘ IDataSource2
Dim storegroup ‘ Variant
Dim mbx ‘ Variant
Dim bFound ‘ Boolean
Dim sMailStorePath ‘ String
Dim sDirectoryServer ‘ String
Dim sDomainName ‘ String‘ Initialize MoveMailbox
MoveMailbox = FalseSet objServer = CreateObject(“CDOEXM.ExchangeServer”)
Set objSG = CreateObject(“CDOEXM.StorageGroup”)
Set objMSDB = CreateObject(“CDOEXM.MailboxStoreDB”)‘ Initialize bFound.
bFound = False‘ Note that although the object is formally known as “IDataSource2”
‘ it is accessed as “IDataSource”
Set iDS = objServer.GetInterface(“IDataSource”)
iDS.Open Moveto_Server‘ Check that the destination mailbox store exists.
For Each storegroup In objServer.StorageGroups
objSG.DataSource.Open storegroup
If UCase(Moveto_StorageGroup) = UCase(objSG.Name) Then
For Each mbx In objSG.MailboxStoreDBs
objMSDB.DataSource.Open mbx
If UCase(Moveto_MailboxStore) = UCase(objMSDB.Name) Then
bFound = True
sMailStorePath = CStr(mbx)
Exit For
End If
Next
End If
If bFound Then Exit For
NextIf Not bFound Then
wScript.echo “The destination MailStore is not found.”
Exit Sub
End If‘ Get the default naming context.
Set rootDSE=GetObject(“LDAP://RootDSE”)
DomainContainer = rootDSE.Get(“defaultNamingContext”)
Set conn = CreateObject(“ADODB.Connection”)
conn.Provider = “ADSDSOObject”
conn.Open “ADs Provider”
ldapStrUsers = “<LDAP://” & DomainContainer & “>;(&(&(& (mailnickname=*) (| (&(objectCategory=person)(objectClass=user)(|(homeMDB=*)(msExchHomeServerName=*))) ))));adspath;subtree”
Set rs = conn.Execute(ldapStrUsers)
While not rs.EOF
Set objUser = GetObject (rs.Fields(0).Value)
strWinMgmts = “winmgmts:{impersonationLevel=impersonate}!//”& _
Server&”/”&cWMINameSpace
Set objWMIExchange = GetObject(strWinMgmts)
‘ Verify we were able to correctly set the object.
If Err.Number <> 0 Then
WScript.Echo “ERROR: Unable to connect to the WMI namespace.”
Else
Set listExchange_Mailboxs = objWMIExchange.InstancesOf(cWMIInstance)
For Each objExchange_Mailbox in listExchange_Mailboxs
If (objExchange_Mailbox.MailboxDisplayName = objUser.displayName) Then
WScript.Echo objUser.DisplayName & “-” & objExchange_Mailbox.Size
If CInt(objExchange_Mailbox.Size) > Oversize Then
WScript.Echo “Moving…” & objUser.displayName
Set objMailbox = objUser
objMailbox.MoveMailbox “LDAP://” & sMailStorePath
‘ Save data into the data source.
objUser.SetInfo
WScript.Echo “Done.”
End If
End If
Next
End Ifrs.MoveNext
WendEnd Sub
Conclusion
Scripts have the reputation for being difficult to write and that they sometimes take longer to write than the manual operation would have taken. I think the scripts in this article show how scripts can be practical, especially in large environments where managing a large number of servers, databases, users and organizational units can become a time consuming operation. Carefully written scripts utilizing various Exchange programming interfaces can really streamline and automate such operations.
If you missed the other articles in this series please read: