Первые 2 части статьи описывают пользователей Exchange со всех сторон. Эта третья часть — о способах доступа к объектам Exchange (серверам, группам хранилищ и базам данных).
Читайте:
- Разработка скриптов для Exchange с использованием VBScript и ADSI (Часть 1)
- Разработка скриптов для Exchange с использованием VBScript и ADSI (Часть 2)
Доступ к объектам Exchange производится при помощи ADSI и CDOEXM. Это описано в предыдущих статьях. Также можно получить доступ к некоторой информации через WMI, интерфейс сценариев, который можно использовать для запросов как к аппаратной, так и к программной части. Таким образом, можно делать запросы к серверу Exchange, его службам и, скажем, к процессору компьютера одновременно.
Перемещение почтовых ящиков
CDOEXM имеет объекты, соответствующие основным объектам Exchange — сервера, группы хранилищ, хранилища почтовых ящиков, и базы данных. Функция «CreateObject» используется для декларации этих объектов, хотя они уже существуют. Сами объекты — это средства доступа к основным объектам. Они не создаются в сценарии.
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
Первые декларации прекрасно объясняют сами себя — в них мы объявляем объекты для пользователя, корня Службы каталогов и объектов Exchange. Однако, могут всплыть следующие вопросы: что такое «IDataSource2» и где же «первый» «IDataSource»?
«IDataSource2» — это интерфейс. Интерфейс — это способ представления объектов Exchange. У каждого интерфейса есть свои собственные методы. Интерфейс «IDataSource2» используется для удаления объектов Exchange. Есть и другие способы удаления объектов Exchange (например, с помощью ADSI). Этот метод еще выполняет и многочисленные проверки и процедуры очистки, которые необходимы для правильного обслуживания хранилища.
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
Использование сценария перемещения почтовых ящиков
Итак, у Вас могли возникнуть мысли о том, как же используется этот сценарий? В конце концов, можно переместить пользователей из одной базы данных Exchange в другую, используя элемент Пользователи и Компьютеры Службы каталогов. Также можно использовать диалог Поиск для группировки пользователей с похожими свойствами. Конечно, использование сценариев может автоматизировать процесс и можно, например, назначить их выполнение на определенное время. Но все же, как можно перемещать пользователей из ветви OU? Определенно, можно выбрать всех пользователей одной OU, щелкнуть на них правой кнопкой мыши и воспользоваться опцией «Задачи Exchange».
Однако, это не переместит пользователей вложенных OU. А если требуется перенести целую ветвь со сложной структурой (например, в целях использования групповых политик), это может оказаться длительным процессом, выполняемым вручную.
Гораздо легче выполнить такие действия при помощи сценария. Чтобы найти всех пользователей Exchange во всех OU и переместить их, можно использовать рекурсию.
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
Получение информации о размерах почтовых ящиков
В настоящее время ни один программный интерфейс не покроет полностью Exchange (в будущей версии Exchange ситуация может измениться). CDO используется для доступа к элементам OUTLOOK и с его помощью можно получить размеры почтовых ящиков через MAPI. Однако, в таком случае необходимо, чтобы пользователь, исполняющий сценарий, имел доступ ко всем почтовым ящикам, а такого права в Exchange 2000/3 по умолчанию у него нет. Такое право есть только у администраторов Exchange. Но, это единственный способ для серверов Exchange версии 2000. В версии Exchange 2003 можно запросить размер почтового ящика через WMI. Зачем? Что это? Для чего изучать еще один интерфейс?
Microsoft разработал WMI как производный интерфейс от SNMP, популярного протокола, предоставляющего единый интерфейс для мониторинга сетевого оборудования. Любое устройство, которое может работать по сети и поддерживает этот протокол, способно предоставлять информацию о сетевой активности, загрузке ЦП, оперативной памяти и т. д. Microsoft дополнили эту модель до уровня приложения и добавил специальные функции, как в других программных интерфейсах.
WMI стал очень популярным, поэтому практически каждый пакет обновления и каждая новая версия серверных продуктов снабжается средствами для работы с WMI. Однако новый интерфейс WMI не поддерживает старые продукты. То есть интерфейс WMI Exchange 2003 не подходит для Exchange 2000. Один из таких интерфейсов позволяет запрашивать размер почтового ящика.
Для доступа к приложению с помощью WMI требуется именное пространство CIM, которое определяет информацию, доступную через приложение.
On Error Resume Next
Dim cComputerName
Const cWMINameSpace = "root/MicrosoftExchangeV2"
Остальная часть сценария запрашивает размеры почтовых ящиков нашего сервера в килобайтах.
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 может показаться трудным в освоении из-за сложной нотации, однако всегда можно скопировать существующие сценарии и изменить их под собственные нужды.
Дилемма группы хранилищ
WMI не предоставляет интерфейса для доступа к группам хранилищ или персональным базам данных. Это значит, что нельзя опросить почтовое хранилище на предмет его почтовых ящиков. Так что же делать, когда нужно создать список почтовых ящиков хранилища?
Всего лишь необходимо отфильтровать результаты работы предыдущего сценария, используя запрос LDAP к Службе каталогов, чтобы выяснить, расположены ли соответствующие почтовые ящики в соответствующем хранилище.
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 = " ">;(&(&(&(& (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
Заключение
Сценарии имеют репутацию сложных и чересчур долгих в написании объектов. Думается, что сценарии, показанные в настоящей статье могут быть полезны, особенно в больших системах, где управление большим количеством серверов, баз данных, пользователей и других организационных объектов может занимать значительное время. Тщательно выверенные сценарии, использующие различные программные интерфейсы Exchange реально могут автоматизировать и ускорить такие операции.