Последние записи.

Изменение паролей для всех пользователей в OU

Powershell Logo
Продолжая разговор о полезностях powershell-а давайте рассмотрим такую задачу.

Дано:
1. Энное количество пользователей-учеников какой нибудь системы обучения
2. При изменении политики паролей вам нужно поменять все пароли на произвольные.

Как бонус нужно отослать письмо о произведенных изменениях в отдел персонала.

Реализация:

Нужное подразделение (OU) или целое дерево условно находится в

$OU = "example.com/Units/"

Имя контейнера пусть будет

Pupils

При запуске скрипта производится обход дерева заданного в переменной $OU и для пользователей всех контейнеров с именем Pupils производится следующее:
1. пользователю создается новый пароль

   1..6 | ForEach { $Password = $Password + [char]$rand.next(97,122) }

97,122 – диапазон используемых символов из таблицы ASCII
2. Данные о смененном пароле записываются в файл строкой.

  Add-Content -Path $file -Value "Пользователь:$(ConvertFrom-DN($DN))`t Имя для входа - $($login)`t Новый пароль - $($Password)"

3. полученные файлы отчетов переименовываются в TXT файлы и отсылаются как вложение в необходимое подразделение. Формирование адреса подразделения производится следующим образом.
1. В функции get-Name в зависимости от имени юнита в котором находится учетная запись пользователя формируем первую часть адреса.

if ($DN.Contains("Unit01")) {$file="Unit1"}

Далее в функции send-Report добавляем к полученному имени окончание

  $ToAddress =  $name + "HRDepartment@example.com"

Собственно сам скрипт:

# change-passwords.ps1

# в зависимости от имени подразделения формируем часть мыла
function get-Name {
param([string]$DN=(Throw ‘$DN is required!’))

  $file = ""
  if ($DN.Contains("Unit01")) {$file="Unit1"}
  if ($DN.Contains("Unit02")) {$file="Unit2"}
 
  $fullPath = $reportFolder+$file
  return $fullPath
}

# конвертируем DN и извлекаем из него имя пользователя
function ConvertFrom-DN {
param([string]$DN=(Throw ‘$DN is required!’))

  foreach ( $item in ($DN.replace(‘\,’,‘~’).split(","))) {
    switch -regex ($item.TrimStart().Substring(0,3)) {
      "CN=" {$CN = ‘/’ + $item.replace("CN=","");continue}
      "OU=" {$ou += ,$item.replace("OU=","");$ou += ‘/’;continue}
      "DC=" {$DC += $item.replace("DC=","");$DC += ‘.’;continue}
    }
  }
  $canoincal = $cn.ToString().replace(‘~’,‘,’)
  $canoincal = $cn.ToString().replace(‘/’,»)
  return $canoincal
}

# меняем расширение файла
Function Rename-FileExtension($path, $newExtension) {
  Get-ChildItem -path $path |
  Foreach-Object {
    if($_.extension.length -gt 0) {
      $baseName = $_.name.remove($_.name.length - $_.extension.length)
    } else {
      $baseName = $_.name }
      Rename-Item -Path $_.fullname -newname ($baseName + $newExtension) -Force
  }
}

# отправка итогового отчета
function send-Report {
param([string]$file=(Throw ‘$messageBody is required!’))

  $name = Split-Path $file -leaf
  $ToAddress =  $name + "HRDepartment@example.com"
  Write-Host "Sendind report to $($ToAddress)"
 
  Rename-FileExtension -path $file -newExtension ".txt"
  $File = "$($file).txt"

  $date = ( get-date ).ToString(‘dd-MM-yyyy’)
  $FromAddress = "password.auditor@example.com"
  $MessageSubject = "Change passwords report"
  $MessageBody = "Change passwords report for $date."
  $SendingServer = "xxx.xxx.xxx.xxx"
               
  $SMTPMessage = New-Object System.Net.Mail.MailMessage $FromAddress, $ToAddress, $MessageSubject, $MessageBody
  $Attachment = New-Object System.Net.Mail.Attachment $File
  $SMTPMessage.Attachments.Add($Attachment)
       
  $SMTPClient = New-Object System.Net.Mail.SMTPClient $SendingServer
  $SMTPClient.Send($SMTPMessage)
}

#
#
$reportFolder="D:\Reports\passwords\"
Get-ChildItem -path $reportFolder | Remove-Item

# OU для обработки
$OU = "example.com/Units/"

Get-QADUser -SizeLimit 0 -SearchRoot $OU | ForEach-Object {
  $DN = $_.DN
  # Если в имени OU есть Pupils то начинаем обработку пользователей которые есть внутри
  if ($DN.Contains("Pupils")) {
    # генерим им пароль
    $Password = ""
    $rand = New-Object System.Random
    1..6 | ForEach { $Password = $Password + [char]$rand.next(97,122) }
    1..2 | ForEach { $Password = $Password + [char]$rand.next(48,57) }

    $file = get-Name($DN)
    $login = $_.LogonName
    Add-Content -Path $file -Value "Пользователь:$(ConvertFrom-DN($DN))`t Имя для входа – $($login)`t Новый пароль – $($Password)"
               
    $account = [ADSI]$ldapURL
    $account.InvokeSet("SetPassword", $Password)
    $account.setinfo()
  } else {
    #Write-Host "Пропускаем и идем дальше"
  }
}

# отправляем отчеты
foreach ($file in gci $reportFolder) {
        $name = split-path $file.fullname -Leaf
        $fullName = $reportFolder + $name
        send-Report $fullName
}

Ошибка Outlook (0×80190194) при загрузке Web Distribution OAB

При попытке Outlook загрузить Web Distribution OAB начала вылезать ошибка (0×80190194).

При ближайшем рассмотрении ошибки было выявлено:

err.exe 0x80190194
# for hex 0x80190194 / decimal -2145844844
BG_E_HTTP_ERROR_404 bitsmsg.h
# 1 matches found for "0x80190194"

Таким образом причина невозможности загрузки OAB заключена в том что Outlook клиент не может ее обнаружить на CAS-сервере.

Загрузка Web Distribution OAB происходит по следующему алгоритму:

  1. Запуск Outlook-а
  2. Outlook-клиент обращается к сервису автообнаружения (autodiscover) и после получения файла autodiscover.xml определяет наличие веб-сервисов Exchange.
  3. Производится анализ и просмотр секций для корпоративной сети или если вы находитесь за пределами своей сети то для Outlook Anywhere.
  4. Выбираем меню «Инструменты – Отправить/Получить – Загрузить адресную книгу».
  5. Outlook определяет OABUrl – http://exchange-01.example.com/OAB/fdc3e24f-c49b-4136-b996-cbd093234585/.
  6. Клиент создает соединение по протоколу https к OABUrl.
  7. Если соединение прошло успешно то клиент пытается загрузить файл OAB.xml.
  8. В файле OAB.xml проверяется необходимость обновления локальной копии OAB.
  9. В случае необходимости происходи обновление локальной копии OAB.

В случае если вы имеете один сервер с совмещенными ролями копирование файлов производится одновременно с генерацией OAB. Если же у вас несколько серверов с разделенными ролями то файлы OAB копируются на CAS-серверы в следующих случаях:

  1. Создана новая OAB.
  2. Сервер генерации OAB изменился.
  3. Истекло значение Default Polling Interval (по умолчанию оно равно 8 часов).
  4. Созданы новые OAB-файлы binpatch.oab.

После завершения репликации в журнале появляется событие 1008

Event Type: Information
Event Source: MSExchangeFDS
Event Category: FileReplication
Event ID: 1008
Date: 09/05/2010
Time: 3:34:46 PM
User: N/A
Computer: Exchange-01
Description:
Process MSExchangeFDS.exe (PID=2484). Offline Address Book data synchronization task has completed successfully. OAB name: "Default Offline Address Book", Guid: fdc3e24f-c49b-4136-b996-cbd093234585

For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.

If the OAB files are missing you will need to find out why the FDS service is not replicating them.

В данном случае проблема приводящая к ошибке 0×80190194 заключалась в том что на серверы CAS не попадала последняя версия OAB из-за проблем репликации средствами FDS.

Решение 1

Перезапустите службу Microsoft Exchange File Distribution Service

net stop MSExchangeFDS
net start MSExchangeFDS

на всех Exchange серверах и проверьте наличие события 1008 в журнале.

Решение 2

Можно использовать как временное до разрешения проблемы с MSExchangeFDS.
С сервера генерации OAB из локальной папки

C:\Program Files\Microsoft\Exchange Server\ExchangeOAB

она же

Путь Unc - \\exchange-01\OAB\

Копируем файлы на сервер клиентского доступа в папку

C:\Program Files\Microsoft\Exchange Server\Client Access\OAB\

Далее остается разобраться с причинами по которым не отрабатывала служба FDS, но это как говориться уже другая история…

Работа Outlook-клиента с OAB

На днях пришлось заниматься восстановлением двух из трех Exchange 2003 серверов у старого знакомого. Первый сервер имел проблемы с дисками, второй сервер оказался вполне живым так как был front-end сервером, с последним же пришлось повозиться потому что основной его проблемой была невозможность генерировать OAB и в завершении картину дополняли сильно поврежденные почтовые базы.

Собственно, речь сегодня пойдет об совершенно отвлеченных вещах, проблемы с генерацией OAB я постараюсь разобрать немного позже.

Как работает Outlook-клиент при загрузке OAB?

После чтения документации и небольших поисков в интернете получилось примерно следующее.
Outlook клиенты загружают OAB один раз в 24 часа, отсчет периода ведется с момента последней успешной загрузки адресной книги, в двух режимах, полная загрузка и разностная загрузка.

Полная загрузка OAB:
Полная загрузка OAB инициируется в случаях:

  1. На клиентском компьютере нет автономной адресной книги по причине того что полная синхронизация ни разу не выполнялась.
  2. Если размер файлов разносного обновления превышает одну восьмую от полного размера текущей версии автономной адресной книги.

Если не вдаваться в терминологию и не рассматривать версии OAB, то процесс происходит по следующему алгоритму:

  • Получение текущей версии OAB на сервере.
  • В случае необходимости полной загрузки OAB по причинам описанным выше она выполняется.
  • В случае если истек период в 24 часа так же выполняется полная загрузка OAB.

Процесс проходит в следующем порядке:

  • В зависимости от установленной локали клиента выбирается файл шаблона, загружается на диск, распаковывается и переименовывается в Tmplts.oab.
  • Загружается файл Details.oab

Заметка: В случае если OAB загружается полностью в папке находится шесть файлов, а при выборе загрузки сокращенной адресной книги (no-details offline address book), у вас будет только пять файлов.

Дифференциальная загрузка OAB:
При начале загрузки Outlook сравнивает порядковый номер последней загруженной версии с порядковым номером файла полной или разностной версии OAB на сервере. В случае если Outlook определяет, что порядковый номер любого из этих файлов выше чем сохраненный в MAPI-профиле, чем у загруженного файла, начинается загрузка, распаковывается полученный файл на диск, объединяется с существующими файлами. Процесс повторяется до тех пор пока номер версии OAB на клиенте не достигнет номера версии OAB на сервере.

В результате всех перечисленных выше действий файлы OAB попадают в папку по умолчанию %userprofile%\Local Settings\Application Data\Microsoft\Outlook

Имена файлов зависят от формата автономной адресной книги (Юникод или ANSI). В папке расположены следующие файлы.

ANSI версии файлов:

  • Файл Anrdex.oab – индекс разрешаемых имен.
  • Файл Browse.oab – файл содержит типы объекта, выводимое имя и указатель в Details.oab файле для других объектов.
  • Файл Details.oab – содержит детальную информацию обо всех объектах исключая только выводимое имя.
  • Файл Pdndex.oab – сведения об изменении имен доменов.
  • Файл Rdndex.oab – индекс разрешения distinguished имен.
  • Файл Tmplts.oab – файл содержит шаблоны диалогов и прочих статических объектов из OAB.

Юникод версии файлов:

  • Файл Uanrdex.oab – Юникод версия файла Anrdex.oab.
  • Файл Ubrowse.oab – Юникод версия файла Browse.oab.
  • Файл Udetails.oab – Юникод версия файла Details.oab.
  • Файл Updndex.oab – Юникод версия файла Pdndex.oab.
  • Файл Urdndex.oab – Юникод версия файла Rdndex.oab.
  • Файл Utmplts.oab – Юникод версия файла Tmplts.oab.

Принудительная установка временной зоны

При работе мобильных клиентов в нескольких поясах появляется необходимость автоматической установки временной зоны на клиенте.

Приведу пример скрипта который выставляет значение временной зоны в GMT+3.
Для автоматизации процесса средствами GPO добавьте скрипт в автозагрузку для «компьютера».

‘ GMT+3.vbs
on error resume next

Set dtmConvertedDate = CreateObject("WbemScripting.SWbemDateTime")

strComputer = "."
Dim oWSH
Set oWSH = CreateObject("WScript.Shell")

Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")

Set colOperatingSystems = objWMIService.ExecQuery ("Select * from Win32_OperatingSystem")

For Each objOperatingSystem in colOperatingSystems
    if objOperatingSystem.BuildNumber=6000 then
        wscript.Quit
    else
        set_timezone()
    end if
Next

sub set_timezone ()

  For Each objOperatingSystem in colOperatingSystems
    Lang=objOperatingSystem.OSLanguage    
  Next
  if Lang=1033 then
    oWSH.Run "RunDLL32 shell32.dll,Control_RunDLL %SystemRoot%\system32\TIMEDATE.cpl,,/Z (GMT+03:00) " &_
    "Moscow, St. Petersburg, Volgograd", 0, False
  else
    oWSH.Run "RunDLL32 shell32.dll,Control_RunDLL %SystemRoot%\system32\TIMEDATE.cpl,,/Z (GMT+03:00) " &_
    "Москва, Санкт-Петербург, Волгоград", 0, False
  end if
end sub

Для временной зоны GMT+4 необходимо заменить кусок кода на:

if Lang=1033 then
    oWSH.Run "RunDLL32 shell32.dll,Control_RunDLL %SystemRoot%\system32\TIMEDATE.cpl,,/Z (GMT+04:00) " &_
    "Yerevan", 0, False
  else
    oWSH.Run "RunDLL32 shell32.dll,Control_RunDLL %SystemRoot%\system32\TIMEDATE.cpl,,/Z (GMT+04:00) " &_
    "Ереван", 0, False
  end if

Для временной зоны GMT+5 необходимо заменить кусок кода на:

if Lang=1033 then
    oWSH.Run "RunDLL32 shell32.dll,Control_RunDLL %SystemRoot%\system32\TIMEDATE.cpl,,/Z (GMT+05:00) " &_
    "Ekaterinburg", 0, False
  else
    oWSH.Run "RunDLL32 shell32.dll,Control_RunDLL %SystemRoot%\system32\TIMEDATE.cpl,,/Z (GMT+05:00) " &_
    "Екатеринбург", 0, False
  end if

Для временной зоны GMT+6 необходимо заменить кусок кода на:

if Lang=1033 then
    oWSH.Run "RunDLL32 shell32.dll,Control_RunDLL %SystemRoot%\system32\TIMEDATE.cpl,,/Z (GMT+06:00) " &_
    "Almaty, Novosibirsk", 0, False
  else
    oWSH.Run "RunDLL32 shell32.dll,Control_RunDLL %SystemRoot%\system32\TIMEDATE.cpl,,/Z (GMT+06:00) " &_
    "Омск, Новосибирск, Алма-Ата", 0, False
  end if

Exchange 2007 ошибки генерации OAB (9348 и 9109)

На днях столкнулся с совершенно замечательным случаем неправильно выставленных разрешений.
История умалчивала как этого удалось достичь и в какой момент времени клиенты перестали загружать OAB.

Попробую произвести анализ и описать проделанные действия.

Симптомы:

1. В Application log присутствуют следующие ошибки:

Первая.

Event Type: Error
Event Source: MSExchangeSA
Event Category: OAL Generator
Event ID: 9384
Date:  9/5/2020
Time:  4:12:21 PM
User:  N/A
Computer: EXCH-01
Description:
OALGen only supports alphanumeric and space characters on the offline address name. The offline address list is not going to be published.
- //o=xxxxxx/cn=addrlists/cn=oabs/cn=Default Offline Address Book

и вторая

Event Type: Warning
Event Source: MSExchangeSA
Event Category: OAL Generator
Event ID: 9109
Date:  9/5/2020
Time:  4:12:21 PM
User:  N/A
Computer: EXCH-01
Description:
OALGen encountered an error ffffffff (internal ID 50506c7) while generating address list '//o=xxxxxx/cn=addrlists/cn=oabs/cn=Default Offline Address Book'.  Check other logged events to see if this is a serious error.
- //o=xxxxxx/cn=addrlists/cn=oabs/cn=Default Offline Address Book

2. Клиенты не могут загрузить Offline Address Book по причине того что она не формируется.

Причины:

Процесс генерации OAB не завершался по причине того что разрешения на Default Global Address List были изменены и OALGEN выполняя запрос к Active Directory для поиска объекта msExchOAB, не мог его завершить.

Возвращаясь к теории вспомним что во время генерации OAB процесс System Attendant, читает legacyExchangeDN этого OAB, в данном случае это

/o=EXMAPLE/cn=addrlists/cn=oabs/cn=Default OAB

и затем преобразует его в GUID который будет использоваться, чтобы создать папку

UNC имя – \\EXCH-01\ExchangeOAB\{GUID}
Путь – c:\program files\Microsoft\Microsoft Exchange\ExchangeOAB\{GUID}

Но из-за проблемы с разрешениями этого не происходило.

Лечение:

Контейнер All Global Address List должен иметь следующие разрешения:

  • SYSTEM разрешить
      Read
      Write
      Create All Child Objects
      Delete All Child Objects
      Open Address List
  • Authenticate Users разрешить
      Read
      Open Address List
      List Contents
  • Exchange Servers разрешить
      Read
      Open Address List

Необходимые разрешения были восстановлены при помощи Powershell-скрипта:

# Первым шагом устанавливает необходимый контейнер
$container = "CN=Default Global Address List,CN=All Global Address Lists,CN=Address Lists Container,CN=EXAMPLE,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=EXAMPLE,DC=COM"

# добавляем необходимые полномочия для учетной записи SYSTEM:
Add-ADPermission $container -User System -AccessRights GenericAll

# добавляем необходимые полномочия для группы Authenticated Users:
Add-ADPermission $container -User "Authenticated Users" -AccessRights GenericRead, ListChildren -ExtendedRights Open-Address-Book

# добавляем необходимые полномочия для группы Exchange Servers:
Add-ADPermission $container -User "Exchange Servers" -AccessRights GenericRead -ExtendedRights Open-Address-Book