######################################################################### ####### # SyncXLS2AD # Версия 2.36xls 25.12.2011 # # Описание: Синхронизация учётных записей пользователей Active Directory с данными по сотрудникам Компании из документа MS Office Excel (.xlsx) # # Илгиз Мамышев (c) 2011 # http://imamyshev.wordpress.com ######################################################################### ####### # Измените переменные под текущую инфраструктуру $XLSSource = "\\ad.demo.ru\dfs\HR\Сотрудники\Сотрудники Компании.xlsx"; $ADSearchBase = "DC=AD,DC=WEBZAVOD,DC=RU"; $SharePath="\\ad.demo.ru\dfs$\Deploy\SyncXLS2AD"; # Общая папка для скрипта, журнала, временных файлов $SmtpServer = "wz-exch01.ad.demo.ru" # Почтовый сервер $SmtpFrom = "[email protected]" $SmtpTo = "[email protected]" # НИЖЕ ЭТОЙ СТРОКИ СКРИПТ НЕ ИЗМЕНЯТЬ! $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" #Запретить вывод сообщений WriteVerbose (для нормальной работы) #======================================================================== ======= "=====================================================================" > $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 в случае ошибки или отсутствия данных для вывода #=== В случае положительного результата - возврат ОДНОЙ строки с данными #======================================================================== ======= function Get-XLS_Data ([string] $full_name = $null, [string] $EmployeeNumber = $null) { $Result = new-Object System.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-Object -begin{$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 #======================================================================== ======= function convertXLSXtoCSV ([string] $xlspath, $sheet = "sheet1") { function INTto26 { 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 } function STRINGto26INT ([string]$s) { ([int[]][char[]]$s.toupper())[-1..-$s.length] | %{$_-64} | %{$m = 1}{$_*$m;$m*=26} | Measure-Object -Sum | Select-Object -ExpandProperty sum } function Int26toSTRING ([int]$i) { $ofs = "" [string][char[]]((INTto26 $i) | %{$_+64}) } $mincol = 16384 $maxcol = 1 $minrow = [int]::MaxValue $maxrow = 1 $shellApplication = new-object -com shell.application $file = Get-Item $xlspath $destination = Split-Path $xlspath if(!(Test-Path "$destination\temp")){ [void] (New-Item -Path $destination -Name temp -ItemType directory) } 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" -Encoding utf8)).sst.si | Select-Object -ExpandProperty t | %{ if($_ -is [System.Xml.XmlElement]){$_."#text"}else{$_} } # $sh = [xml](Get-Content "$destination\temp\xl\worksheets\$sheet.xml" -Encoding utf8) $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 -TypeName PSObject -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 -TypeName psobject 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 -Encoding utf8 } #=== НАЧАЛО import-module ActiveDirectory $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-Object System.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-object psobject | add-member noteproperty "№" "$Count4" -passthru | add-member noteproperty "Full Name" ($b[1].GivenName + " " + $b[1].Surname + " уволен(а) с " + $b[1].Dismissed) -passthru | add-member noteproperty "Last logon" $LL passthru | add-member noteproperty "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-object psobject | add-member noteproperty "№" "$GETXLSData_recommend_Counter" -passthru | add-member noteproperty "DistinguishedName" $_.DistinguishedName -passthru | addmember noteproperty "Last logon" $LL -passthru | add-member noteproperty "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-object psobject | add-member noteproperty "№" "$Count3" -passthru | add-member noteproperty "EmployeeNumber" $_.EmployeeNumber -passthru | add-member noteproperty "Было (AD)" ($_.Surname + " " + $_.GivenName + " " + $_.Initials) passthru | add-member noteproperty "Стало (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-Object System.Net.Mail.SmtpClient $Message = New-Object System.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-Object System.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)) #=== КОНЕЦ