Скрипт для массового изменения учетных записей электронной почты в Outlook Express и Microsoft Outlook.

Мне несколько раз за свою карьеру системного администратора приходилось выполнять миграцию учетных записей из одного домена в другой при помощи ADMT. Этот процесс хорошо и подробно описан в документах Microsoft, например: в “ADMT v3.1 Guide: Migrating and Restructuring Active Directory Domains”

Но одной миграцией учетных записей AD дело не всегда ограничивается, зачастую требуется перенастройка пользовательского окружения для смигрированных пользовательских учетных записей. Так, однажды, необходимо было решить задачу по массовому изменению настроек учетных записей электронной почты в Outlook Express и Microsoft Outlook: требовалось изменить в учетных записях электронной почты имя SMTP и IMAP серверов. (Вопрос – «почему нельзя было внести изменения в DNS, вместо того, чтобы городить огород с массовым изменением настроек клиентов электронной почты?» – оставим за кадром. ;))

Возможно, что для внесения изменений в учетные записи MS Outlook можно было бы использовать prf-файлы, но совершено точно, что их нельзя было бы использовать для внесения изменений в учетные записи электронной почты Outlook Express. Поэтому я в очередной раз решил обратиться к сриптам и сначала написал простой скрипт для изменения учетных записей Outlook Express, а затем, на его основе, – и чуть более сложный для Microsoft Outlook.

И так, «погуглив» или воспользовавшись Procmon от Русиновича (сейчас уже не помню;)), я выяснил, что параметры реестра, хранящие настройки учетных записей Outlook Express, находятся в разделе HKEY_CURRENT_USER\Software\Microsoft\Internet Account Manager\Accounts

 

Настройки учетных записей, созданных пользователем, расположены в «номерных» подразделах (имеющих название 00000001, 00000002 и т.д.) При создании очередной новой учетной записи Outlook Express в реестре будет создан очередной «последний номерной+1» раздел. При этом не важно, какого типа  («Почты», «Новостей» или «Службы каталогов») учетную запись вы создаете. Так, например, в подразделе реестра HKEY_CURRENT_USER\Software\Microsoft\Internet Account Manager\Accounts0000001 могут храниться параметры учетной записи «Службы каталогов», а  в HKEY_CURRENT_USER\Software\Microsoft\Internet Account Manager\Accounts0000002 – «Почты».

И так, казалось бы, все просто, структура исходных данных понятна, алгоритм – элементарен. Я уже думал, что за пару минут набросаю скрипт на JScript, но тут-то и выяснилось, что «пришла беда, откуда не ждали»: WSH не имеет средств для выполнения перечисления подключей (подразделов) реестра, и для этого придется использовать WMI. И, если на VBScript это не вызывает никаких проблем, так, для того чтобы получить перечень подразделов реестра HKEY_CURRENT_USER\Software\Microsoft\Internet Account Manager\Accounts, можно использовать такой скрипт:

Const HKEY_LOCAL_MACHINE 	= &H80000002
Const HKEY_CURRENT_USER 	= &H80000001

strComputer = "."

Set oReg=GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _
    strComputer & "\root\default:StdRegProv")

strKeyPath = "Software\Microsoft\Internet Account Manager\Accounts"
oReg.EnumKey HKEY_CURRENT_USER, strKeyPath, arrSubKeys

For Each subkey In arrSubKeys
    Wscript.Echo subkey
Next

,то на JScript сделать по аналогии не получится, т.к.
«Normally, direct access is adequate to call a WMI provider method. Direct access means executing a method by using object.method syntax. However, in some cases, direct access cannot be used. For example, JScript does not support output parameters so the return value cannot be directly assigned to a variable.» Поэтому, код на Jscript, код будет выглядеть следующим образом:

var WshShell = WScript.CreateObject ("WScript.Shell");
var SubKeys;
var arrSubKeys=new Array ();
var strOutlookAccounts = "Software\\Microsoft\\Internet Account Manager\\Accounts";

var HKEY_LOCAL_MACHINE	= 0x80000002;
var HKEY_CURRENT_USER 	= 0x80000001;

var strComputer = ".";
oReg=GetObject("winmgmts:{impersonationLevel=impersonate}!\\\\" + strComputer + "\\root\\default:StdRegProv");

var method = oReg.Methods_.Item("EnumKey");
var inParams = method.InParameters.SpawnInstance_();
inParams.hDefKey = HKEY_CURRENT_USER;
inParams.sSubKeyName = strOutlookAccounts;
var outParams = oReg.ExecMethod_(method.Name, inParams);
arrSubKeys = outParams.sNames.toArray();

for (var i=0;i<arrSubKeys.length;i++)
{
    WScript.Echo(arrSubKeys[i]);
}

Подробнее об этом можно прочитать здесь: Writing WMI Scripts in JScript, Constructing InParameters Objects and Parsing OutParameters Objects

Т.к. кусок кода, который выполняет перечисление разделов реестра нам еще пригодится для  скрипта, работающего с Microsoft Outlook, то логично было бы оформить его в виде функции, что и было проделано, см. функцию function EnumSubKeys (эта функция, как некоторые другие, реализующие работу  с реестром, была опубликована на одном из форумов, к сожалению, ссылку на оригинальный пост автора я сейчас найти не смог, поэтому дам ссылку на копию)

И так, вот и результат: скрипт, выполняющий изменение SMTP и IMAP серверов в учетных записях Оutlook Express

//////////////////////////////////////////////////////////////////////////////////
// shs 20071114
// JScript ChangeOEMailServers.js
// Замена текущих значений SMTP и IMAP серверов в учетных записях Outlook Express
//
// Пример вызова (не реализовано! параметры задаются в теле скрипта, а не передаются в командной строке!):
//cscript ChangeOEMailServers.js <old_SMTP_server> <new_SMTP_server> [<old_IMAP_server> <new_IMAP_server>]
//////////////////////////////////////////////////////////////////////////////////
var WshShell = WScript.CreateObject ("WScript.Shell");
//
var arrAccounts; //
var SubKeys;
var strMailAccounts = "HKCU\\Software\\Microsoft\\Internet Account Manager\\Accounts";
var strComputer = ".";
//
if ((WScript.Arguments.Count()!=4|WScript.Arguments.Count()!=6) & false) //false - заглушка
{
  WScript.Echo ("Ошибка! Неправильное количество аргументов. \n"+
  "Пример вызова: cscript ChangeOutlookSMTPIMAPservers.js <old_SMTP_server> <new_SMTP_server> <old_IMAP_server> <new_IMAP_server> [<old_LDAP_server> <new_LDAP_server>]");
}
else
{
  //Заглушка (пока работаем без параметров командной строки)
  var arrTypeOfServer = new Array ("SMTP Server", "IMAP Server") ; //, "LDAP Server");
  var arrOldServer = new Array();
  var arrNewServer = new Array();
  arrOldServer[0] = "oldmailserver";   // WScript.Arguments.Item(0); //старый SMTP-сервер
  arrOldServer[1] = "oldmailserver";  // WScript.Arguments.Item(2);  //старый IMAP-сервер
//  arrOldServer[2]  = "ldap";  // WScript.Arguments.Item(4); //старый LDAP-сервер
  arrNewServer[0] = "newmailserver";   // WScript.Arguments.Item(1); //новый SMTP-сервер
  arrNewServer[1] = "newmailserver";  // WScript.Arguments.Item(3);  //новый IMAP-сервер
//  arrNewServer[2]  = "ldap";   // WScript.Arguments.Item(5); //новый LDAP-сервер

  var strOldLDAPServer, strNewLDAPServer;
  strOldLDAP=strNewLDAP="";

	//Выполняем перечисление учетных записей электронной почты
	arrAccounts = EnumSubKeys(strComputer, strMailAccounts);
	for (var iAccNdx=0;iAccNdx<arrAccounts.length;iAccNdx++)
	{
		for (var iTOSNdx in arrTypeOfServer)
		{
			var strServer=""
			//в каждой учетной записи пытаемся прочитать значение параметра, содержащееся в массиве arrTypeOfServer
			try
			{
				strServer = WshShell.RegRead(strMailAccounts+"\\"+arrAccounts[iAccNdx]+"\\"+arrTypeOfServer[iTOSNdx]);
			}
			catch(e){}
			if (strServer!="")
			{
				if (arrOldServer[iTOSNdx]==strServer)
				{
				  WScript.Echo(strMailAccounts+"\\"+arrAccounts[iAccNdx]+"\\"+arrTypeOfServer[iTOSNdx]);
				  WScript.Echo("Found: " +arrTypeOfServer[iTOSNdx]+" = "+strServer+"...");
					//Заменяем старое название сервера на новое
					WshShell.RegWrite(strMailAccounts+"\\"+arrAccounts[iAccNdx]+"\\"+arrTypeOfServer[iTOSNdx], arrNewServer[iTOSNdx],"REG_SZ");
					WScript.Echo("...and changed to: " +arrTypeOfServer[iTOSNdx]+" = "+arrNewServer[iTOSNdx]+"\n");
				}
			}
		}
	}//end for
}
//------------------------------------------------------------------------------
//Эта функция выполняет перечисление подключей (подразделов) реестра
//(для заданного компьютера и заданного ключа (раздела)  реестра)
//Приходится использовать WMI, т.к. WSH не имеет средств для выполнения данной задачи
function EnumSubKeys (strComputer,RegKey)
{
  var RootKey = new Object()
  RootKey["HKCR"] = RootKey["HKEY_CLASSES_ROOT"]   = 0x80000000;
  RootKey["HKCU"] = RootKey["HKEY_CURRENT_USER"]   = 0x80000001;
  RootKey["HKLM"] = RootKey["HKEY_LOCAL_MACHINE"]  = 0x80000002;
  RootKey["HKUS"] = RootKey["HKEY_USERS"]          = 0x80000003;
  RootKey["HKCC"] = RootKey["HKEY_CURRENT_CONFIG"] = 0x80000005;
  var RootVal = RootKey[RegKey.substr(0, RegKey.indexOf("\\"))]
  if (RootVal != undefined)
  {
  	var oReg=GetObject("winmgmts:{impersonationLevel=impersonate}!\\\\" + strComputer + "\\root\\default:StdRegProv");
  	var method = oReg.Methods_.Item("EnumKey");
  	var inParams = method.InParameters.SpawnInstance_();
	inParams.hDefKey = RootVal;
	inParams.sSubKeyName = RegKey.substr(RegKey.indexOf("\\") + 1);
	var outParams = oReg.ExecMethod_(method.Name, inParams);
	return outParams.sNames.toArray();
  }
}

PS Скрипт немного не доведен до ума (я поленился реализовать передачу параметров из командной строки в скрипт, оставив пару «заглушек» и определив значения входных параметров в теле скрипта), но вполне работоспособный.

Продолжение следует…

6 Comments

  1. Pingback: Скрипт для массового изменения учетных записей электронной почты в Outlook Express и Microsoft Outlook (часть 2) « ShS's Blog

  2. доброе время суток!

    >Вопрос – «почему нельзя было внести изменения в DNS, вместо того, чтобы городить огород с >массовым изменением настроек клиентов электронной почты?» – оставим за кадром.

    вы имеете ввиду mx-запись в локальной зоне? для меня сейчас очень актуален вопрос автоматической смены адресов pop3, imap, и smtp серверов в настройках outlook express клиентов. Только желательно без применения скриптов. Не могли бы вы немного развить свою мысль про днс?

    • Нет, mx запись тут не причем. mx-запись используется почтовыми серверами, а не клиентами. Почтовый клиент испольует тот адрес (или DNS-имя smtp-севрера), который ему было явно указан в конкретной учетной записи электронной почты. Предположим, что у вас в DNS имеется, например, такая запись MyMailServer.mydomain.local A 10.0.0.1, и в учетных записях ваших почтовых клиентов smtp-сервер указан, как MyMailServer.mydomain.local. Вы хотите, что бы ваши клиенты перстали пользоваться сервером 10.0.0.1, а вместо него стали использовать 10.0.0.2. В этом случае все, что вам нужно сделать, это внести изменния в DNS так, чтобы вышеупомянутая A-запись указывала на новый сервер: MyMailServer.mydomain.local A 10.0.0.2. При этом, вам ничего не придется менять на стороне клиента.

      • Спасибо за ответ, действительно все просто )) Можно еще сделать маппинг портов, если первоначально адреса в аутлуках указывали на почтовый сервер на шлюзе.

  3. День добрый.
    Если у моих клиентов часть машин используют доступ по POP3, а часть по IMAP, можно применять этот скрипт? Имя сервера в обоих случаях совпадает.

    • Очевидно вам нужно будет внести изменения в скрипт. Обратите внимание на следующие строки:
      //Заглушка (пока работаем без параметров командной строки)
      var arrTypeOfServer = new Array (“SMTP Server”, “IMAP Server”) ; //, “LDAP Server”);

      Массив arrTypeOfServer содержит перечень параметров реестра, которые будут проверены скриптом. В дааном случае скрипт проверит только записи для IMAP и SMTP сервера. вам необходимо добавить в массив название параметра реестра, в котором OE хранит имя POP-сервера. Как называется этот параметр – не знаю, но по аналогии, наверное, – “POP server” (вы можете посмотреть это на своем компьютере при помощи regedit).

Leave a Reply

Your email address will not be published. Required fields are marked *

Notify me of followup comments via e-mail. You can also subscribe without commenting.