Scripting Exchange Using VBScript and ADSI (Part 3)

If you missed the other articles in this series please read:

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.MailboxStoreDB





    Dim 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 = False

    Set 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
    Next













    If 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.SetInfo

        wscript.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.MailboxStoreDB





    Dim iDS             ‘ IDataSource2

    Dim storegroup      ‘ Variant
    Dim mbx             ‘ Variant
    Dim bFound          ‘ Boolean
    Dim sMailStorePath   ‘ String
    Dim sDirectoryServer ‘ String
    Dim sDomainName      ‘ String




    ‘ Initialize MoveMailbox
    MoveMailbox = False

    Set 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
    Next













    If 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, sMailStorePath

End Sub

Sub MoveOUUsers (oObject,Path)
Dim objUser, objContainer

For 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 Sub
















Function 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 server

Dim 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 object


Set 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.”
Else





  Set 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.Size

    Next
  Else

    WScript.Echo “WARNING: No Exchange_Mailbox instances were returned.”
  End If
End If

Function 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 = True

conn.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.MailboxStoreDB








    Dim iDS             ‘ IDataSource2

    Dim storegroup      ‘ Variant
    Dim mbx             ‘ Variant
    Dim bFound          ‘ Boolean
    Dim sMailStorePath   ‘ String
    Dim sDirectoryServer ‘ String
    Dim sDomainName      ‘ String




    ‘ Initialize MoveMailbox
    MoveMailbox = False

    Set 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
    Next













    If 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 If






























              rs.MoveNext
      Wend

End 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:

Leave a Comment

Your email address will not be published.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Scroll to Top