Главная > PowerShell > Актуализация данных в Active Directory (версия XLSX)

Актуализация данных в Active Directory (версия XLSX)

Перейдем сразу к телу.
Данная заметка является продолжением темы «Актуализация данных в Active Directory» и представляет новую — вторую версию скрипта.
В первой версии в качестве источника данных выступала SQL база данных системы управления персоналом «БОСС – Кадровик» и PowerShell командлеты для работы с Active Directory от Quest Software.
В этой — второй версии скрипта в качестве источника данных выступает Excel-файл формата .xlsx и PowerShell со штатными командлетами работы с Active Directory в WIndows Server 2008 R2.

Итак, имеем:
— Active Directory, Windows Server 2008 R2, PowerShell с установленными командлетами по управлению Active Directory
— «Источник данных» — Excel-файл — список сотрудников (пример)
— Специально обученный офис менеджер или секретарь, вручную обновляющий информацию в «Источнике данных»

Необходимо:
— периодически обновлять некоторую информацию о пользователях в каталоге Active Directory на основе информации, полученной из «Источника данных»
— журналировать произведенные изменения
— высылать по электронной почте отчёт о работе и рекомендации системному администратору

Решение:
Скрипт на PowerShell, реализующий заданные требования.
Скрипт можно выполнять интерактивно, или через планировщик задач, например командой: %windir%\system32\WindowsPowerShell\v1.0\powershell.exe -command ‘\\ad.demo.ru\dfs$\Deploy\SyncXLS2AD\SyncXLS2AD.ps1’ .

Особенность скрипта состоит в том, что чтение данных из Excel-файла производится без использования приложения Microsoft Office Excel.

Далее представлен сам скрипт «как есть».
Скачать скрипт целиком в виде текстового файла.

################################################################################

# SyncXLS2AD

# Версия 2.36xls 25.12.2011

#

# Описание: Синхронизация учётных записей пользователей Active Directory с данными по сотрудникам Компании из документа MS Office Excel (.xlsx)

#

# Илгиз Мамышев (c) 2011

# https://imamyshev.wordpress.com

################################################################################

# Измените переменные под текущую инфраструктуру

$XLSSource=«\\ad.demo.ru\dfs\HR\Сотрудники\Сотрудники Компании.xlsx»;

$ADSearchBase=«DC=ad,DC=demo,DC=ru»;

$SharePath=«\\ad.demo.ru\dfs$\Deploy\SyncXLS2AD»; # Общая папка для скрипта, журнала, временныхфайлов

$SmtpServer=«exch01.ad.demo.ru»                 # Почтовый сервер

$SmtpFrom=«SyncXLS2AD@demo.ru»

$SmtpTo=«logs@demo.ru»

# НИЖЕ ЭТОЙ СТРОКИ СКРИПТ НЕ ИЗМЕНЯТЬ!

$ScriptName=«SyncXLS2AD»;

$Count= 1; $Count2= 1; $Count3= 1; $Count4= 1;   # переменные для различных счетчиков

$script:GETXLSData_recommend_Counter= 0;

$RecomTextLN= @();

$RecomTextDSBL= @();

$GETXLSData_recommend= @();

$LogFile=«$SharePath\$ScriptName»+«.log»                 # Файл журнала работы скрипта

$RulesFile=«$SharePath\$ScriptName»+«_RULES.txt»   # Файл с правилами работы скрипта

$Subject=«Актуализация данных в Active Directory — РЕКОМЕНДАЦИИ АДМИНИСТРАТОРУ»

$VerbosePreference=«Continue»#Разрешить вывод сообщений Write-Verbose (для проведения отладки)

$VerbosePreference=«SilentlyContinue»#Запретить вывод сообщений Write-Verbose (для нормальной работы)

#===============================================================================

«=====================================================================» > $RulesFile

«СИНХРОНИЗАЦИЯ УЧЕТНЫХ ЗАПИСЕЙ ПОЛЬЗОВАТЕЛЕЙ ACTIVE DIRECTORY» >> $RulesFile

«С ДАННЫМИ ПО СОТРУДНИКАМ КОМПАНИИ ИЗ EXCEL СПИСКА СОТРУДНИКОВ» >> $RulesFile

«» >> $RulesFile

«`t`t`tО С Н О В Н Ы Е    П Р А В И Л А» >> $RulesFile

«» >> $RulesFile

«1. НАПРАВЛЕНИЕ СИНХРОНИЗАЦИИ EXCEL (.xlsx) ФАЙЛ — > ACTIVE DIRECTORY» >> $RulesFile

«Требования к Excel файлу-Источнику:» >> $RulesFile

«- выполняется чтение только первого листа документа;» >> $RulesFile

«- первая строка воспринимается как заголовок документа с именами колонок, очерёдность колонок значения не имеет;» >> $RulesFile

«- обязательные имена колонок и пример\формат заполнения» >> $RulesFile

«ИМЯ КОЛОНКИ      ФОРМАТ            НАЗНАЧЕНИЕ» >> $RulesFile

«EmplID           123456            Табельный номер»  >> $RulesFile

«Surname          Иванов            Фамилия» >> $RulesFile

«GivenName  Иван        Имя» >> $RulesFile

«MiddlenameИванович    Отчество» >> $RulesFile

#»BirthDay  12.12.1234  Деньрождения» >> $RulesFile

«Title            Инженер           Должность» >> $RulesFile

«DepartmentДППР        Подразделение» >> $RulesFile

«Company          ООО ‘Марс’  Компания» >> $RulesFile

«Office           123         Кабинет» >> $RulesFile

«OfficePhone      +7(846)1234567    Рабочийтелефон» >> $RulesFile

«IPPhone          123         Внутреннийтелеон» >> $RulesFile

«MobilePhone      +7(937)1234567    Мобильныйтелефон» >> $RulesFile

«Manager          ПётрПетровFullName учётнойзаписиМенеджерав Acrive Directory» >> $RulesFile

«Dismissed  12.12.1234  Дата увольнения» >> $RulesFile

«- все поля должны быть текстовыми, без форматирования» >> $RulesFile

«2. СОЕДИНЕНИЕ данных Excel и Active Directory выполняется по ‘ИМЯ ФАМИЛИЯ’ (поле ‘FullName’) или по табельному номеру (поле ‘EmployeeNumber’)» >> $RulesFile

«3. Поля учетной записи пользователя домена, подлежащие обновлению:» >> $RulesFile

«- Общие\Фамилия» >> $RulesFile

«- Общие\Имя» >> $RulesFile

«- Общие\Инициалы» >> $RulesFile

«- Общие\Офис» >> $RulesFile

«- Общие\Телефон» >> $RulesFile

«- EmployeeNumber (Табельный номер)» >> $RulesFile

«- Организация\Должность» >> $RulesFile

«- Организация\Отдел» >> $RulesFile

«- Организация\Организация» >> $RulesFile

«- Организация\Менеджер» >> $RulesFile

«- Телефоны\Мобильный» >> $RulesFile

«- Телефоны\IP телефон» >> $RulesFile

«4. В случае изменения полей для учетной записи пользователя, найденного в Excel файле, будут обновлены только изменившиеся поля.» >> $RulesFile

«5. В случае изменения фамилии сотрудника, если учетная запись пользователя ранее была синхронизирована и было заполнено поле EmployeeNumber (Табельный номер),» >> $RulesFile

«в результате синхронизации будет выдана рекомендация по корректировке учетной записи пользователя.» >> $RulesFile

«6. Если сотрудник уволен, — выводимое имя в свойствах учетной записи пользователя будет откорректировано (дописано ‘- уволен..’).» >> $RulesFile

«и будет выдана рекомендация по отключению учетной записи.» >> $RulesFile

«7. Отключенные учетные записи не обновляются.» >> $RulesFile

«8. Все произведенные изменения в Active Directory журналируются.» >> $RulesFile

«9. Рекомендации по ручной корректировке Active Directory отправляются электронным письмом.» >> $RulesFile

«» >> $RulesFile

«Системный инженер И.Мамышев» >> $RulesFile

«=====================================================================» >> $RulesFile

#=== ФУНКЦИИ

#===============================================================================

#=== Получить данные сотрудника по Имени и Фамилии

#=== Результат $null в случае ошибки или отсутствия данных для вывода

#=== В случае положительного результата — возврат ОДНОЙ строки с данными

#===============================================================================

functionGet-XLS_Data ([string] $full_name=$null, [string] $EmployeeNumber=$null)

{

      $Result=new-ObjectSystem.Collections.ArrayList;

      if ($full_name-ne$null)

      {

            #=== Разбор FULL_NAME. Выделим только первые ДВА слова (Имя и Фамилия). Если меньше — не принимаем к обработке. Если больше — усекаем что больше первых 2-х слов.

            Write-Verbose«Строка ‘Имя Фамилия’ ДО обработки: `t`t$full_name»

            if ($full_name.split(» «).Count -lt 2) {Write-Verbose«Передана некорректная строка в качестве Имени Фамилии»; return$false};

            $full_name.split(» «) | ForEach-Objectbegin{$c=0; $full_name=«»} —process{$c++; if($c-lt 2) {$full_name+=$_+» «}; if($c-eq 2) {$full_name+=$_} }

            Write-Verbose«Строка ‘Имя Фамилия’ ПОСЛЕ обработки: `t$full_name»;

            # Делаем выборку данных о сотруднике по Имени Фамилии ($full_name)

        $Result= ( Import-Csv-Path$script:CSVSource-Delimiter«;» | Where-Object {(($_.GivenName).Trim()+» «+($_.Surname).Trim()) -eq$full_name} );

      }elseif($EmployeeNumber-ne$null)

      {

            # Делаем выборку данных о сотруднике по Табельному Номеру ($EmployeeNumber)

            $Result= ( Import-Csv-Path$script:CSVSource-Delimiter«;» | Where-Object {$_.EmplID -eq$EmployeeNumber} );

      }else{ Write-Verbose«Не переданы Имя и Фамилия или Табельный номер в качестве параметра!»; return$null; };

      # Обрабатываем результат

      if ($Result-ne$null)

      {

            $Result | ForEach-Object-Begin {$i=0} -Process {$i++} -End {$i};

            if ($i-gt 1)

            {

                  Write-Verbose$Result;

                  Write-Verbose«Результатов больше чем 1. Прерываем процедуру.»;

                  return$null;

            };

      }else

      {

            Write-Verbose«Нет данных для вывода по запрошенному Имя Фамилия ($full_name)!»;

            $script:GETXLSData_recommend_Counter++;

        return$null;

      };

      # В результирующем наборе ОДИН сотрудник и его данные

      # Обработаем данные (срежем с концов строк возможные пробелы и точки)

      $Result.Surname =$Result.Surname.Trim();

      $Result.GivenName =$Result.GivenName.Trim();

      $Result.MiddleName =$Result.MiddleName.Trim();

      $Result.Title =$Result.Title.Trim();

          $Result.Title =$Result.Title.TrimEnd(«.»);

      $Result.Department =$Result.Department.Trim();

          $Result.Department =$Result.Department.TrimEnd(«.»);

      $Result.Company =$Result.Company.Trim();

          $Result.Company =$Result.Company.TrimEnd(«.»);

      $Result.OfficePhone =$Result.OfficePhone.Trim();

      $Result.IPPhone =$Result.IPPhone.Trim();

      $Result.MobilePhone =$Result.MobilePhone.Trim();

      $Result.Manager =$Result.Manager.Trim();

      $Result.Email =$Result.Email.Trim();

    if ($Result.Dismissed.Length -le 8) {$Result.Dismissed =$null; } else {$Result.Dismissed = Get-Date($Result.Dismissed) -Format «dd.MM.yyyy»}; # Датаувольнения

return$Result

}

#===============================================================================

#=== Конвертирование файла формата .XLSX в файл формата .CSV

#=== без использования ПО MS Office Excel

#=== Вызов функции: convertXLSXtoCSV «C:\file1.xlsx» [sheet1|sheet2|..]

#=== На выходе одноименный файл в формате CSV: C:\file1.csv

#===============================================================================

functionconvertXLSXtoCSV ([string] $xlspath, $sheet=«sheet1»)

{

      functionINTto26

      {

            param ([int]$x)

            $m=$x% 26

          if($x-gt 0 -and$m-eq 0){$m= 26}

          if(($x$m) -gt 0)

            {

              $d= ($x$m)/26

              $dummy= 0

              intto26$d

          }

          $m

      }

      functionSTRINGto26INT ([string]$s)

      {

          ([int[]][char[]]$s.toupper())[1..$s.length] | %{$_64} |

            %{$m= 1}{$_*$m;$m*=26} | Measure-Object-Sum |

            Select-Object-ExpandPropertysum   

      }

      functionInt26toSTRING ([int]$i)

      {

          $ofs=«»

          [string][char[]]((INTto26$i) | %{$_+64})

      }

 

      $mincol= 16384

      $maxcol= 1

      $minrow= [int]::MaxValue

      $maxrow= 1

      $shellApplication=new-object-comshell.application

      $file=Get-Item$xlspath

      $destination=Split-Path$xlspath

      if(!(Test-Path«$destination\temp»)){ [void] (New-Item-Path$destination-Nametemp-ItemTypedirectory) }

      Rename-Item$xlspath«$xlspath.zip»

      $zipPackage=$shellApplication.NameSpace(«$xlspath.zip»)

      $destinationFolder=$shellApplication.NameSpace(«$destination\temp»)

      $destinationFolder.CopyHere($zipPackage.Items().item(2))

      #

      $sharedstr= ([xml] (Get-Content«$destination\temp\xl\sharedStrings.xml»-Encodingutf8)).sst.si |

          Select-Object-ExpandPropertyt | %{ if($_-is [System.Xml.XmlElement]){$_.«#text»}else{$_} }

      #

      $sh= [xml](Get-Content«$destination\temp\xl\worksheets\$sheet.xml»-Encodingutf8)

      $basedata=$sh.worksheet.sheetData | %{$_.row} | %{$_.c} | %{

          $col=$_.r -replace«\d+»,«»

          if((STRINGto26INT$col) -gt$maxcol){$maxcol= (STRINGto26INT$col)}

          if((STRINGto26INT$col) -lt$mincol){$mincol= (STRINGto26INT$col)}

          $row=$_.r -replace«[a-z]+»,«»

          if([int]$row-gt [int]$maxrow){$maxrow=$row}

          if([int]$row-lt [int]$minrow){$minrow=$row}

          $value=if($_.t -eq«s»){$sharedstr[($_.v)]}elseif($_.t -ne«E»){$_.v}

          New-Object-TypeNamePSObject-Property @{col =$col; row =$row; value =$value}

      }

      #

      Remove-Item«$destination\temp»-Confirm:$false-Force-Recurse

      Rename-Item«$xlspath.zip»$xlspath

      #

      $h= @{}

      $mincol..$maxcol | %{Int26toSTRING$_} | %{$h.$_=«»}

      $th= @{}

      $minrow..$maxrow | %{$th.$_=New-Object-TypeNamepsobject-Property$h}

      $basedata | %{ ($th.([int]$_.row)).($_.col) =$_.value }

      #

      $th.keys | Sort-Object |%{$th.$_}|

          Select-Object-Property ($mincol..$maxcol | %{Int26toSTRING$_} ) |

              Export-Csv-Path ($xlspath-replace‘xlsx$’,«csv») -NoTypeInformation-UseCulture-Encodingutf8

}

#=== НАЧАЛО

import-moduleActiveDirectory

$dtstart=Get-Date-Format«dd.MM.yyyy HH:mm:ss»

«======================================================`n

Синхронизация Active Directory с Excel-списком сотрудников`n

Источник данных (Excel-список сотрудников): «+$XLSSource+«`n

Отчет о изменениях: «+$LogFile+«`n

Начало: «+$dtstart+«`n»>> $LogFile

«——————————————————« >> $LogFile

$RecomText+=«РЕКОМЕНДАЦИИ по итогам Синхронизации Active Directory с Excel-списком сотрудников: $XLSSource </br>

Отчет о произведенных изменениях в Active Directory: $LogFile </br>

Правила работы процесса синхронизации: $RulesFile </br>

Начало: $dtstart </br>

——————————————————————— </br>

Измените свойства учетных записей в соответствии с информацией\рекомендациями ниже, затем повторите или дождитесь следующей синхронизации. </br>»

cls

Write-Host«Синхронизация Active Directory с Excel-списком сотрудников»

Write-Host«Пожалуйста подождите..»

# Подготовим копию файла Источника для импорта

Copy-Item$XLSSource$SharePath

$XLSSource=$SharePath+«\»+ (Split-Path$XLSSource-leaf)

if (Test-Path$XLSSource)

{

    # Преобразуем XLS файлв CSV

    convertXLSXtoCSV$XLSSource

    $CSVSource=$XLSSource-replace‘xlsx$’,«csv»; # Путь к результату convertXLSXtoCSV

}else

{

    $RecomText=«Неудаётсянайтифайл $XLSSource. Выполнениепрервано

    «ERROR: «+$RecomText >> $LogFile

    Write-Host$RecomText

    exit

}

# Удалим первую строку из файла (строка с именами столбцов: A,B,C,D..)

$File=Get-Content-Path$CSVSource

$File=$File[1..$($File.Count 1)]

$File > $CSVSource

# Подготовим список учётных записей из Active Directory

$ADUsers=( Get-ADUser -Filter { (Name -like«*») } -SearchBase $ADSearchBase -SearchScope Subtree  -Properties *  | Where {$_.DistinguishedName -notlike«*Service Accounts*»} | Where {$_.DistinguishedName -notlike«*Official Accounts*»} | Where {$_.DistinguishedName -notlike«*Test Accounts*»} )

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

$ADUsers |

      ForEach-object-Begin {if($ADUsers.Count -le 0){$ADUCnt=1}else{$ADUCnt=$ADUsers.Count}; $percent=$ADUCnt/100; $c= 0; «Будет обработано $ADUCnt учетных записей» >> $LogFile} -Process {

      $c++; [int]$a=$c/$percent; Write-Progress-Activity«Идетобработка…»-PercentComplete$a-CurrentOperation«$a% завершено»-Status«Пожалуйстаподождите.»; #Счетчик — процентвыполнения

      #=== Пробуем получить данные по сотруднику по ФИ ($_.Name)

      # Мы здесь даже тогда, когда поле ТАБЕЛЬНЫЙ НОМЕР заполнено, но данные в запросе к Источнику не нашли

      # Если учетная запись пользователя в AD ВЫКЛЮЧЕНА, то выходим из цикла и идем к следующей итерации

      if ([bool]($_.Enabled -eq$false)) {Write-Verbose«$_.Name выключен, переход к следующему циклу.»; return; }

      $Count2++

      # Запросим данные о сотруднике из Источника по Имени Фамилии

      $b=New-ObjectSystem.Collections.ArrayList

      $b= Get-XLS_Data($_.Name, $null)

      Write-Verbose«$($_.EmployeeNumber) $($_.Name) $($_.UserPrincipalName) Счетчик: $Count2″

      if ($b-ne$null)

      {

            # Информация из Источника по Имя Фамилия получена, подготовим данные

        if ($b[1].Surname.Length -gt 64) {$b[1].Surname=$b[1].Surname.SubString(0,64)}

            if ($b[1].GivenName.Length -gt 64) {$b[1].GivenName=$b[1].GivenName.SubString(0,64)}

            if ($b[1].Title.Length -gt 64) {$b[1].Title=$b[1].Title.SubString(0,64)}

            if ($b[1].Department.Length -gt 64) {$b[1].Department=$b[1].Department.SubString(0,64)}

            if ($b[1].Company.Length -gt 64) {$b[1].Company=$b[1].Company.SubString(0,64)}

            if ($b[1].MiddleName.Length -gt 64) {$b[1].MiddleName=$b[1].MiddleName.SubString(0,64)}

            if ($b[1].OfficePhone.Length -gt 64) {$b[1].OfficePhone=$b[1].OfficePhone.SubString(0,64)}

            if ($b[1].IPPhone.Length -gt 64) {$b[1].IPPhone=$b[1].IPPhone.SubString(0,64)}

            if ($b[1].MobilePhone.Length -gt 64) {$b[1].MobilePhone=$b[1].MobilePhone.SubString(0,64)}

            $Initials=$b[1].GivenName[0] +«.»+$b[1].MiddleName[0] +«.»;

            $OfficePhone=$b[1].OfficePhone +«x»+$b[1].IPPhone;

            $DisplayName=$b[1].GivenName +» «+$b[1].Surname;

            $Manager=$b[1].Manager;

            if ( ($Manager-eq$null) -or ($Manager.Length -le 1) ) {$Manager=«»}else {$Manager= Get-ADUser -Filter {Name -eq$Manager}}

            $dt=Get-Date-Format«dd.MM.yyyy HH:mm:ss»;

            $notes=«Синхронизировано ($ScriptName) с Источником «+$dt

            #=== Подготовимпараметрыдля Set-ADUser

            $doUpdate=$false;

        $Parameters= @{};

        if([string]$_.Surname -ne [string]$b[1].Surname) {$Parameters[«-Surname»] =$b[1].Surname; $doUpdate=$true;};

        if([string]$_.GivenName -ne [string]$b[1].GivenName) {$Parameters[«-GivenName»] =$b[1].GivenName; $doUpdate=$true;};

        if($DisplayName.Length -ne 0) {$Parameters[«-DisplayName»] =$DisplayName};

        # Если параметр изменился и он не пустой (сравниваем значение из Источника и из AD), то будем обновлять

        if( ([string]$_.Initials -ne [string]$Initials) -and ($Initials.Length -ne 0) ) {$Parameters[«-Initials»] =$Initials; $doUpdate=$true;};

        if( ([string]$_.Office -ne [string]$b[1].Office) -and ($b[1].Office.Length -ne 0) ) {$Parameters[«-Office»] =$b[1].Office; $doUpdate=$true;};

        if( ([string]$_.EmployeeNumber -ne [string]$b[1].EmplID) -and ($b[1].EmplID.Length -ne 0) ) {$Parameters[«-EmployeeNumber»] =$b[1].EmplID; $doUpdate=$true;};

        if( ([string]$_.Title -ne [string]$b[1].Title) -and ($b[1].Title.Length -ne 0) ) {$Parameters[«-Title»] =$b[1].Title; $doUpdate=$true;};

        if( ([string]$_.Department -ne [string]$b[1].Department) -and ($b[1].Department.Length -ne 0) ) {$Parameters[«-Department»] =$b[1].Department; $doUpdate=$true;};

        if( ([string]$_.Company -ne [string]$b[1].Company) -and ($b[1].Company.Length -ne 0) ) {$Parameters[«-Company»] =$b[1].Company; $doUpdate=$true;};

        if( ([string]$_.OfficePhone -ne [string]$OfficePhone) -and ($OfficePhone.Length -ne 0) ) {$Parameters[«-OfficePhone»] =$OfficePhone; $doUpdate=$true;};

        if( ([string]$_.MobilePhone -ne [string]$b[1].MobilePhone) -and ($b[1].MobilePhone.Length -ne 0) ) {$Parameters[«-MobilePhone»] =$b[1].MobilePhone; $doUpdate=$true;};

        if( ([string]$_.Manager -ne [string]$Manager.DistinguishedName) -and ($b[1].Manager.Length -ne 0) ) {$Parameters[«-Manager»] =$Manager.DistinguishedName; $doUpdate=$true;};

            # Некоторые параметры можем записать\обновить только через Add, Replace

            $Replace= @{Info=$notes;}

        if( ([string]$_.ipPhone -ne [string]$b[1].IPPhone) -and ($b[1].IPPhone.Length -ne 0) ) {$Replace=$Replace+ @{ipPhone=$b[1].IPPhone}; $doUpdate=$true;};

        if($Replace.Count -gt 0) {$Parameters[«-Replace»] =$Replace;};

            # Если параметр пустой и он изменился (сравниваем значение из Источника и из AD), то будем его удалять

        $Remove= @{}

            if( ([string]$_.Office -ne [string]$b[1].Office) -and ($b[1].Office.Length -eq 0) ) {$Remove=$Remove+ @{physicalDeliveryOfficeName=$_.Office}; $doUpdate=$true;};

        if( ([string]$_.EmployeeNumber -ne [string]$b[1].EmplID) -and ($b[1].EmplID.Length -eq 0) ) {$Remove=$Remove+ @{employeeNumber=$_.EmployeeNumber}; $doUpdate=$true;};

        if( ([string]$_.Title -ne [string]$b[1].Title) -and ($b[1].Title.Length -eq 0) ) {$Remove=$Remove+ @{title=$_.Title}; $doUpdate=$true;};

        if( ([string]$_.Department -ne [string]$b[1].Department) -and ($b[1].Department.Length -eq 0) ) {$Remove=$Remove+ @{department=$_.Department}; $doUpdate=$true;};

        if( ([string]$_.Company -ne [string]$b[1].Company) -and ($b[1].Company.Length -eq 0) ) {$Remove=$Remove+ @{company=$_.Company}; $doUpdate=$true;};

        if( ([string]$_.OfficePhone -ne [string]$OfficePhone) -and ($OfficePhone.Length -eq 0) ) {$Remove=$Remove+ @{telephoneNumber=$_.OfficePhone}; $doUpdate=$true;};

        if( ([string]$_.MobilePhone -ne [string]$b[1].MobilePhone) -and ($b[1].MobilePhone.Length -eq 0) ) {$Remove=$Remove+ @{mobile=$_.MobilePhone}; $doUpdate=$true;};

        if( ([string]$_.Manager -ne [string]$Manager.DistinguishedName) -and ($b[1].Manager.Length -eq 0) ) {$Remove=$Remove+ @{manager=$_.Manager}; $doUpdate=$true;};

        if( ([string]$_.ipPhone -ne [string]$b[1].IPPhone) -and ($b[1].IPPhone.Length -eq 0) ) { $Remove=$Remove+ @{ipPhone=$_.ipPhone}; $doUpdate=$true;};        

            if($Remove.Count -gt 0) {$Parameters[«-Remove»] =$Remove;}

        if($doUpdate-eq$true)

            {

                  # Один или несколько параметров учётной записи изменились, требуется обновление

                  Try

                  {

                        $_ | Set-ADUser @Parameters;

                  }

                  Catch

                  { # Catch Errors

                        Write-Verbose$_

                        Write-Verbose«!!! Ошибка Set-ADUser, переходимкследующемуциклу.»;

                        return;

                  }

                  «» >> $LogFile;

                  «»+$Count+«. Обновленысвойства: «+$_.DistinguishedName >> $LogFile;

                  «Было`t=>LastName:»+$_.Surname +«,`tFirstName:»+$_.GivenName +«,`tInitials:»+$_.Initials +«,`tOffice:»+$_.Office +«,`tEmployeeNumber:»+$_.EmployeeNumber +«,`tTitle:»+$_.Title +«,`tDepartment:»+$_.Department +«,`tCompany:»+$_.Company +«,`tOfficePhone:»+$_.OfficePhone+«,`tMobilePhone:»+$_.MobilePhone +«,`tIPPhone:»+$_.ipPhone +«,`tManager:»+$_.Manager >> $LogFile

                  «Стало`t=>LastName:»+$b[1].Surname +«,`tFirstName:»+$b[1].GivenName +«,`tInitials:»+$Initials+«,`tOffice:»+$b[1].Office +«,`tEmployeeNumber:»+$b[1].EmplID +«,`tTitle:»+$b[1].Title +«,`tDepartment:»+$b[1].Department +«,`tCompany:»+$b[1].Company +«,`tOfficePhone:»+$OfficePhone+«,`tMobilePhone:»+$b[1].MobilePhone +«,`tIPPhone:»+$b[1].IPPhone +«,`tManager:»+$Manager.DistinguishedName >> $LogFile

                  $Count++

            }

            $dt=Get-Date

            $dt=$dt.ToShortDateString()

            if( ($b[1].Dismissed -ne$null)-and($dt-gt$b[1].Dismissed) )

            {

                  # Сотрудник уволен. Обновим Выводимое имя пользователя

                  $NewDisplayName=$b[1].GivenName +» «+$b[1].Surname +» — уволен(а) с «+$b[1].Dismissed

                  $_ | Set-ADUser -DisplayName $NewDisplayName

                  «»+$_.DistinguishedName +«`n Сотрудникуволен. Выводимое Имя сменено на: «+$NewDisplayName >> $LogFile

                  # Отключим учетную запись пользователя, если она не отключена

                  if ([bool]($_.Enabled -eq$true) )

                  {

                        if ( $_.LastLogonDate -eq$null ) {$LL=«Нетданных»}else{$LL=$_.LastLogonDate}

                  }

            $RecomTextDSBL+= (new-objectpsobject | add-membernoteproperty«№»«$Count4»-passthru | add-membernoteproperty«Full Name» ($b[1].GivenName +» «+$b[1].Surname +» уволен(а) с «+$b[1].Dismissed) -passthru | add-membernoteproperty«Last logon»$LL-passthru | add-membernoteproperty«Department»$_.Department -passthru)        

                  #$_ | Set-ADUser -Enabled $false; $RecomTextDSBL += «Действие: Отключенаучетнаязапись $_.DistinguishedName`n»

                  $Count4++

            }

      }elseif($b-eq$null)

      {

            # Информация в Источнике ЗАПРАШИВАЛАСЬ по Имя Фамилия и НЕ БЫЛА ПОЛУЧЕНА, готовим инфу для лога рекомендаций

            if ([bool]($_.Enabled -eq$true) )

            {

                  if ( $_.LastLogonDate -eq$null ) {$LL=«Нетданных»}else{$LL=$_.LastLogonDate}

            }

        $GETXLSData_recommend+= (new-objectpsobject | add-membernoteproperty«№»«$GETXLSData_recommend_Counter»-passthru | add-membernoteproperty«DistinguishedName»$_.DistinguishedName -passthru | add-membernoteproperty«Last logon»$LL-passthru | add-membernoteproperty«Department»$_.Department -passthru)

            # Не нашли по Имя Фамилия и Если заполнено поле табельный номер, то пробуем получить из Источника данные сотрудника по табельному номеру

            if ($_.EmployeeNumber.Length -gt 0)

            {

                  $b2= Get-XLS_Data($null, $_.EmployeeNumber);

                  if($b2-ne$null)

                  {

                        $Initials=$b2[1].GivenName[0] +«.»+$b2[1].MiddleName[0] +«.»

                        Write-Verbose«$($_.EmployeeNumber) $($_.Name) $($_.UserPrincipalName)»

                        if( ([string]$b2[1].Surname -ne [string]$_.Surname)-and([string]$b2[1].GivenName -eq [string]$_.GivenName)-and([string]$Initials-eq [string]$_.Initials) )

                        {

                             # Если нашли данные в Источнике по табельному номеру, ИМЯ и ИНИЦИАЛЫ совпадают , а ФАМИЛИЯ в учётной записи АД НЕ совпадает с ФАМИЛИЕЙ из Источника, то

                             # Готовимрекомендацию — СМЕНИЛАСЬФАМИЛИЯПОЛЬЗОВАТЕЛЯ

                    $RecomTextLN+= (new-objectpsobject | add-membernoteproperty«№»«$Count3»-passthru | add-membernoteproperty«EmployeeNumber»$_.EmployeeNumber -passthru | add-membernoteproperty«Было (AD)» ($_.Surname +» «+$_.GivenName +» «+$_.Initials) -passthru | add-membernoteproperty«Стало (Excel)» ($b2[1].Surname +» «+$b2[1].GivenName +» «+$Initials) -passthru)

                             $Count3++

                        }

                  }else

                  {

                        #=== Записанный в учетке Табельный номер не найден в Источнике, удалим Таб.номер как неверный

                        Write-Verbose«Табельный номер, указанный в AD не существует в Источнике!»

                        Set-ADUser -Identity $_.DistinguishedName -EmployeeNumber $null

                        «Табельный номер , указанный в AD не существует в Источнике. Значениебудетудалено!» >> $LogFile

                        «Было`t=> `tDistinguishedName:`t»+$_.DistinguishedName +«`tEmployeeNumber:`t»+$_.EmployeeNumber >> $LogFile

                  }

            }

      }

      } # ForEach-object -Process

#================================================================

«Обновлено «+ ($Count 1) +» учетныхзаписейпользователей Active Directory» >> $LogFile

$dt=Get-Date-Format«dd.MM.yyyy HH:mm:ss»

«Конец: «+$dt >> $LogFile

# Готовим тексты рекомендаций

if ($RecomTextLN.Length -gt 0)

{

      $RecomText+=«——————————————————————— </br>»

      $RecomText+=«Список сотрудников, у которых изменилась фамилия.</br>»

      $RecomText+=«Если рекомендации в части ИЗМЕНЕНИЯ ФАМИЛИИ неверны — удалите содержимое поля ‘EmployeeNumber’ свойств учетной записи пользователя. </br>»

      $RecomText+= ($RecomTextLN | ConvertTo-Html)

}

if ($RecomTextDSBL.Length -gt 0)

{

      $RecomText+=«——————————————————————— </br>»

      $RecomText+=«Следующие сотрудники уволены, — рекомендуется отключить уч. записи: </br>»

      $RecomText+= ($RecomTextDSBL | ConvertTo-Html)

    Write-Host$RecomTextDSBL

}

if ($GETXLSData_recommend.Length -gt 0)

{

      $RecomText+=«——————————————————————— </br>»

      $RecomText+=«Следующие Имя Фамилия не были обнаружены при запросе к Источнику, возможно их необходимо откорректировать: </br>»

      $RecomText+= ($GETXLSData_recommend | ConvertTo-Html)

}

$RecomText+=«Конец: «+$dt+«`n»

Write-Progress-Activity«Working…»-Completed-Status«All done.»#Отображение индикатора выполнения завершим. Пропадет с экрана.

#=== Отправка EMail

Write-Verbose«Создаем объекты SmtpClient и MailMessage»

$SmtpClient=New-ObjectSystem.Net.Mail.SmtpClient

$Message=New-ObjectSystem.Net.Mail.MailMessage

$SmtpClient.Host =$SmtpServer

$Message.IsBodyHtml =$True

$Message.Body =$RecomText

$Message.Subject =$Subject

$Message.From =$SmtpFrom

[string[]]$To=$SmtpTo

[System.IO.FileInfo[]]$Attachment=@() #  [System.IO.FileInfo[]]$Attachment= (dir c:\*.txt)

Write-Verbose«Создаем и добавляем вложения»

$Attachment | ForEach-Object {

    $a=New-ObjectSystem.Net.Mail.Attachment($_.fullname)

    $Message.Attachments.Add($a)

}

Write-Verbose«Добавляемполучателей«

$To | ForEach-Object {$Message.To.Add($_)}

Write-Verbose«Отправляемсообщение«

$smtpclient.Send($Message)

$Message.Dispose()

#

Write-Host«Синхронизациязавершена

Write-Host«Отчет о произведенных изменениях в Active Directory: $LogFile»

Write-Host«Рекомендации по ручной корректировке базы данных AD высланы на $SmtpTo»

Write-Verbose«Удаляем временные файлы»

#Remove-Item $CSVSource

#Remove-Item ($SharePath + «\» + (Split-Path $XLSSource -leaf))

#=== КОНЕЦ


Пример отправляемого письма с рекомендациями и пример файла журнала произведенных изменений:

image

Реклама
Рубрики:PowerShell
  1. 07.02.2012 в 15:08

    Илгиз добрый день.
    Этот пост, почему-то не вытаскивается по rss в Windows Live Mail 2011 (Version 2011 Build 15.4.3538.0513). Outlook-ом 2010 вытаскивается нормально.
    Есть идеи почему? А то обидно, пропустил хороший пост.

  2. 07.02.2012 в 18:49

    Не знаю..
    По https://imamyshev.wordpress.com/rss.xml и https://imamyshev.wordpress.com/feed в браузере вроде как полностью видно всё.
    Может пост слишком длинный? 🙂

  3. Алексей
    09.02.2012 в 14:30

    Выдает ошибку скрипт при преобразовании xlsx в csv

    Нельзя вызвать метод для выражения со значением NULL.
    C:\Scripts\syncxls2ad.ps1:184 знак:47
    + $destinationFolder.CopyHere($zipPackage.Items <<<< ().item(2))
    + CategoryInfo : InvalidOperation: (Items:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    скрипт скачан по ссылке выше, где копать?

    • 09.02.2012 в 14:34

      Проверьте, что в первой строке Excel -документа (имена колонок) ячейки не пустые, а имеют значения.

  4. Алексей
    09.02.2012 в 14:54

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

  5. Андрей
    24.02.2012 в 09:39

    Илгиз, добрый день.
    У меня не понятная проблема. Скрипт запускается, работает, создает правильный csv, но не вносит изменения пользователям в AD. Ошибок нет, куда капать не понятно.

    • 24.02.2012 в 16:55

      Андрей, этот скрипт — не решение, готовое под ключ в чью-то (кроме как в мою) продакшен среду.
      Материал предоставлен «как есть», и поддержку по нему я не планирую предоставлять.
      Разбирайтесь, комментарии в скрипте есть, вывод сообщений для отладки тоже предусмотрен.
      Возможно у учётной записи, под которой выполняется скрипт, не хватает привилегий на внесение изменений в Active Directory.

      • Андрей
        27.02.2012 в 07:24

        да я понимаю, просто ни каких сообщений о некорректной работе не выводит, куда копать не ясно.
        есть ощущение что при разборе csv скриптом, скрипт не видит данные. возможно чего-то не хватает из программного обеспечения? так как если делать привязку по табельному номеру — все время ругается что в ИСТОЧНИКЕ нет такого табельного номера и удаляет его у пользователя, хотя в csv он есть.

  6. Андрей
    27.02.2012 в 15:17

    все спасибо. разобрался.

    • Иван
      23.05.2014 в 18:31

      поделитесь пожалуйста такая же проблема

      • Константин
        08.09.2014 в 12:21

        Та-же проблема, скрипт работает, ошибок не выдает, но и изменений в AD не вносит. Подскажите куда копать?)

  7. fbird
    28.05.2012 в 14:50

    Спасибо, очень полезный скрипт. А можно туда докрутить еще загрузку фотографий в AD для отображения в Outlook и Lync? Было бы очень удобно. Фотографии в нужном формате лежат в папке на сети, формат jpg, имя файла совпадает с логином. Моих знаний, к сожалению, не хватает (((

    • 14.07.2014 в 16:14

      Надо перед «#=== Подготовим параметры для Set-ADUser » добавить строку чтото вроде
      $photo = [byte[]](Get-Content \\server\\share\_$.SamAccountName.jpg -Encoding byte); if ($photo -ne $null) (Set-ADUser _$.SamAccountName -Server dcname.contoso.com -Replace @{thumbnailPhoto=$photo})

      Можно конечно и затолкать _$.SamAccountName -Server dcname.contoso.com -Replace @{thumbnailPhoto=$photo} в @Parameters, но мне это показалось сложнее

  8. Salavat
    20.12.2013 в 12:02

    Адреса электронной почты не обновляются

  9. Nick
    06.03.2014 в 12:35

    Почему то не добавляются адреса электронной почты и не обновляются

    • 23.05.2014 в 18:38

      Скрипт не обновляет это поле в AD. В описании есть перечень полей AD которые скрипт обновляет, email туда не входит.

  10. 11.07.2014 в 13:37

    Добрый день. Использовал этот скрипт, даже слегка доработал и все было супер, пока в файле xls было до 192 строк(или около того). С добавлением следующего сотрудника произошел какой то сдвиг изменяемых данных. То есть стали подставляться данные из других ячеек и даже строк или просто пустые в хаотичном порядке. Наверно какие то ограничения powershell сработали или что-то подобное?

    • 14.07.2014 в 13:13

      Оказывалось в файле источнике один и тот же человек был указан 2 раза. В самом скрипте от такой ситуации встроена защита, но почему то она не сработала. Ищу причину.

      • 14.07.2014 в 15:40

        Нашел и у себя исправил. Может кому то пригодится.
        if ($b -ne $null)
        — вот эта защита в строке 268 не срабатывает в случае 2х одинаковых строк в XLSx. Дело в том, что в функции Get-XLS_Data выполняется return $null, но на самом деле возвращается все равно пустой массив, который не равен $null. Предлагаю эту строку заменить на
        if (($b| Measure-Object).Count -gt 1)
        В случае когда данных в csv файле нет, ($b| Measure-Object).Count равен 0,
        в случае 2х одинаковых строк равен 1,
        Когда данные есть в наличии равен 2.

  11. Максим
    04.12.2014 в 13:16

    Скрипт работает, ошибок не выдает, но и изменений в AD не вносит.В чем может быть проблема?привелегий у учётки под которой запускается скрипт хватает

  1. No trackbacks yet.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s

%d такие блоггеры, как: