Архив

Archive for Январь 2009

Актуализация данных в Active Directory

Имеем:
— Active Directory
— систему управления персоналом «БОСС — Кадровик»

Необходимо:
— периодически обновлять некоторую информацию о пользователях в каталоге Active Directory на основе информации, полученной из системы «БОСС — Кадровик».

Решение:
Скрипт на PowerShell, реализующий заданные требования.
Скрипт можно выполнять интерактивно, или через планировщик задач, например командой — «C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe -Command D:\Sync_AD_BOSS.ps1«.
Для работы скрипта также понадобятся PowerShell Commands (CMDLETs) for Active Directory by Quest Software.

Как говорится — «Как есть».
Из него Вам пригодятся ключевые моменты по реализации той или иной логики.
====================================================================
Далее код PowerShell.
#===============================================================================
# СИНХРОНИЗАЦИЯ УЧЕТНЫХ ЗАПИСЕЙ ПОЛЬЗОВАТЕЛЕЙ ACTIVE DIRECTORY
# С ДАННЫМИ ПО СОТРУДНИКАМ КОМПАНИИ ИЗ СИСТЕМЫ БОСС Кадровик
# НАПРАВЛЕНИЕ СИНХРОНИЗАЦИИ БОСС Кадровик -> ACTIVE DIRECTORY
# СОЕДИНЕНИЕ ПО «ФАМИЛИЯ ИМЯ ОТЧЕСТВО» или по табельному номеру
#
# ОАО ПКК «Весна». Илгиз Мамышев
# Версия 17.06.2008
#===============================================================================
#=== Инициализация переменных ===
$Count = 1; $Count2 = 1; $Count3 = 1; $Count4 = 1;
$script:BOSS_Data_by_FN_recomend_Counter = 0;
$script:BOSS_Data_by_FN_recomend = «»;
$LogFile = «\\domen.local\dfs2$\Deploy\Sync_AD_BOSS\SynchronizationActiveDirectoryWithBOSS.log»
$RulesFile = «\\domen.local\dfs2$\Deploy\Sync_AD_BOSS\SynchronizationActiveDirectoryWithBOSS_RULES.txt»
$SmtpServer = «exchsrv.domen.ru»
$SmtpFrom = «alerts@domen.ru»
$SmtpTo = «alerts@domen.ru»
$Subject = «Синхронизация Active Directory и БОСС-Кадровик. РЕКОМЕНДАЦИИ АДМИНИСТРАТОРУ.»
#$VerbosePreference = «Continue» #Разрешить вывод сообщений Write-Verbose
$VerbosePreference = «SilentlyContinue» #Запретить вывод сообщений Write-Verbose
#===============================================================================
«=====================================================================» > $RulesFile
«СИНХРОНИЗАЦИЯ УЧЕТНЫХ ЗАПИСЕЙ ПОЛЬЗОВАТЕЛЕЙ ACTIVE DIRECTORY» >> $RulesFile
«С ДАННЫМИ ПО СОТРУДНИКАМ КОМПАНИИ ИЗ СИСТЕМЫ БОСС Кадровик» >> $RulesFile
«» >> $RulesFile
«`t`t`tО С Н О В Н Ы Е    П Р А В И Л А» >> $RulesFile
«» >> $RulesFile
«1. НАПРАВЛЕНИЕ СИНХРОНИЗАЦИИ БОСС Кадровик — > ACTIVE DIRECTORY» >> $RulesFile
«2. СОЕДИНЕНИЕ ПО ‘ФАМИЛИЯ ИМЯ ОТЧЕСТВО’ (поле ‘Выводимое имя’) или по табельному номеру (поле ‘Адрес\Почтовый ящик’)» >> $RulesFile
«3. Поля учетной записи пользователя домена, подлежащие обновлению:» >> $RulesFile
«- Общие\Фамилия» >> $RulesFile
«- Общие\Имя» >> $RulesFile
«- Общие\Инициалы» >> $RulesFile
«- Общие\Описание» >> $RulesFile
«- Общие\Веб страница (Уровень сотрудника(руководителя))» >> $RulesFile
«- Адрес\Почтовый ящик (Табельный номер)» >> $RulesFile
«- Организация\Должность» >> $RulesFile
«- Организация\Отдел» >> $RulesFile
«- Организация\Организация» >> $RulesFile
«4. В случае изменения полей фамилия, имя, инициалы, описание, веб страница, должность, отдел, организация» >> $RulesFile
«для учетной записи пользователя, найденного в БОСС кадровик по ФИО, будут обновлены все поля, подлежащие синхронизации.» >> $RulesFile
«5. В случае изменения фамилии сотрудника, если учетная запись пользователя ранее была синхронизирована и было заполнено поле Адрес\Почтовый ящик (Табельный номер),» >> $RulesFile
«то в результате синхронизации будет выдана рекомендация по корректировке учетной записи пользователя.» >> $RulesFile
«6. Если сотрудник уволен, — выводимое имя в свойствах учетной записи пользователя будет откорректировано (дописано ‘- уволен..’).» >> $RulesFile
«и будет выдана рекомендация по отключению учетной записи.» >> $RulesFile
«7. Отключенные учетные записи не обновляются.» >> $RulesFile
«8. Все произведенные изменения в Active Directory логируются.» >> $RulesFile
«9. Рекомендации по ручной корректировке Active Directory отправляются электронным письмом.» >> $RulesFile
«» >> $RulesFile
«Замечания, пожелания, предложения — ст. администратор ОЭ УИТ ФД И.Мамышев» >> $RulesFile
«=====================================================================» >> $RulesFile
#================== ФУНКЦИИ ======================================
#===============================================================================
#=== Получить данные сотрудника по ФИО
#=== Результат FALSE в случае ошибки или отсутствия данных для вывода.
#=== В случае положительного результата — возврат строки с данными
#===============================================================================
function Get-BOSS_Data_by_FN ([string]$full_name=»»)
{
if ($full_name.length -eq 0) {Write-Verbose «Не переданы ФИО в качестве параметра»; return $false}
#=== Разбор FULL_NAME. Выделим только первые ТРИ слова (ФИО). Если меньше — не принимаем к обработке. Если больше — усекаем что больше первых 3-х слов.
Write-Verbose «ФИО До обработки: `t`t$full_name»
if ($full_name.split(» «).Count -lt 3) {Write-Verbose «Передана некорректная строка в качестве ФИО»; return $false}
$full_name.split(» «) | ForEach-Object -begin{$c=0; $full_name=»»} -process{$c++; if($c -le 2) {$full_name += $_+ » «}; if($c -eq 3) {$full_name += $_} }
Write-Verbose «ФИО После обработки: `t$full_name»
#========= Подключаемся к серверу БД и читаем их базы данных данные о сотрудниках
#=== В результирующем наборе ОДИН сотрудник и его данные по основному месту работы — сотрудники — уникальны — повторяющихся нет (НО! — исключения есть — парочка)
$SQLCn = New-Object System.Data.SqlClient.SqlConnection( «Server=VSN_SRV4;Trusted_Connection=Yes;Database=Lite»)
$SQLCn.Open()
$SQLCMD = $SQLCn.CreateCommand()
$SQLCMD.CommandText = «SELECT distinct
(case
when people.id_firm = 9 then ‘э’+people.NUM_TAB
when people.id_firm = 10 then ‘л’+people.NUM_TAB
else ‘в’ +people.NUM_TAB end) as EmplID,    — ТАБЕЛЬНЫЙ НОМЕР \ ПОЧТОВЫЙ ЯЩИК
people.id_firm as FirmID,
card.Full_Name,            — ФИО \ ВЫВОДИМОЕ ИМЯ
card.Name,                — ФАМИЛИЯ \ ФАМИЛИЯ
card.Name_i,            — ИМЯ \ ИМЯ
LEFT(card.Name_i,1)+’.’+LEFT(card.Name_o,1)+’.’ as Initials,    — ИНИЦИАЛЫ \ ИНИЦИАЛЫ
card.date_birth as BirthDate,
card.typUdost as IdentityCardCode_ru,
card.passp_ser as IdentityCardSeries_ru,
card.passp_num as IdentityCardNumber_ru,
card.passp_date as IdentityCardIssueDate_ru,
card.passp_grant as IdentityCardIssueBy_ru,
card.addr_zip as ZipCode,            — ИНДЕКС \ ПОЧТОВЫЙ ИНДЕКС
card.addr_city as City,                — ГОРОД \ ГОРОД
card.addr_street as Street,            — УЛИЦА \ УЛИЦА
card.addr_house as House,            — ДОМ \ УЛИЦА + УЛИЦА
card.addr_block as Block,            — КОРПУС \ УЛИЦА + УЛИЦА + УЛИЦА
card.addr_flat as Flat,                — КВАРТИРА \ УЛИЦА + УЛИЦА + УЛИЦА + УЛИЦА
appointments.name_appoint as Title,    — ДОЛЖНОСТЬ \ ДОЛЖНОСТЬ
(case when structs.struct_lev =0  then structs_r.struct_name else structs.struct_name end) as Otdel,    — ПОДРАЗДЕЛЕНИЕ \ ОТДЕЛ
structs.tree_path,                    — ПОЛНЫЙ ПУТЬ \ ОПИСАНИЕ
setup.Short_name as Company,        — КОМПАНИЯ \ ОРГАНИЗАЦИЯ
people.in_date, — ДАТА ПРИЕМА НА РАБОТУ
people.out_date, — ДАТА УВОЛЬНЕНИЯ
(case when people.out_date = ‘2099-01-01′ then 1 else 0 end) as Works,    — РАБОТАЕТ \ ОТКЛЮЧИТЬ УЧЕТНУЮ ЗАПИСЬ
pr_group_value.string as EmplLevel    — УРОВЕНЬ \
FROM card
LEFT OUTER JOIN people (NOLOCK) on card.Auto_Card = people.Auto_Card
LEFT OUTER JOIN PR_CURRENT (NOLOCK) ON PEOPLE.PID = PR_CURRENT.pId
LEFT OUTER JOIN appointments (NOLOCK) on appointments.code_appoint = pr_current.code_appoint
JOIN structs on people.struct_code=structs.struct_code
JOIN structs structs_r on structs.struct_root=structs_r.struct_code
JOIN setup on setup.id_firm = people.id_firm
LEFT OUTER JOIN pr_group_value on pr_current.cell_item = pr_group_value.id_ref and pr_group_value.id_group = 147
WHERE PR_CURRENT.flag_last=’*’
AND people.id_firm in (1,9,10) — Выводим сотрудников только этих компаний
AND card.Full_Name = @full_name
AND out_date = (select max(out_date)
from people people1
where people.auto_card = people1.auto_card
and people1.id_firm in (1,9,10))
and pr_current.Work_Code = (select min(Work_Code)
from pr_current pr_current1, people people1
where pr_current1.pid = people1.pid
and pr_current1.auto_card=card.auto_card
and flag_last=’*’
and people1.out_date = (select max(out_date)
from people people2
where people1.auto_card = people2.auto_card
and people2.id_firm in (1,9,10)))»
$P = $SQLCMD.Parameters.AddwithValue(«@full_name», $full_name)
$SQLResult = $SQLCMD.ExecuteReader() # В этой переменной результат запроса к базе БОСС кадровик
#While ( $SQLResult.Read() ){Write-Host $SQLResult.Item(«EmplId») $SQLResult.Item(«Name»)}

if($SQLResult.Read() -eq $false) {    Write-Verbose «Нет данных для вывода по запрошенному ФИО ($full_name)!»;
$script:BOSS_Data_by_FN_recomend_Counter++;
$script:BOSS_Data_by_FN_recomend += «» + $script:BOSS_Data_by_FN_recomend_Counter + «. $full_name»;
return 100}
$out = @{EmplId=$SQLResult.Item(«EmplId»);
Full_Name=$SQLResult.Item(«Full_Name»);
Name=$SQLResult.Item(«Name»);
Name_i=$SQLResult.Item(«Name_i»);
Initials=$SQLResult.Item(«Initials»);
BirthDate=([datetime]$SQLResult.Item(«BirthDate»)).ToShortDateString();
ZipCode=$SQLResult.Item(«ZipCode»);
City=$SQLResult.Item(«City»);
Street=[string]$SQLResult.Item(«Street»)+», дом «+[string]$SQLResult.Item(«House»)+», блок «+[string]$SQLResult.Item(«Block»)+», квартира «+[string]$SQLResult.Item(«Flat»);
Title=$SQLResult.Item(«Title»);
Otdel=$SQLResult.Item(«Otdel»);
tree_path=$SQLResult.Item(«tree_path»);
Company=$SQLResult.Item(«Company»);
out_date=$SQLResult.Item(«out_date»);
Works=$SQLResult.Item(«Works»);
EmplLevel=$SQLResult.Item(«EmplLevel»);
}
$SQLCN.Close()
return $out
}
#===============================================================================
#===============================================================================
#=== Получить данные сотрудника по табельному номеру
#=== Результат FALSE в случае ошибки или отсутствия данных для вывода.
#=== В случае положительного результата — возврат строки с данными
#===============================================================================
function Get-BOSS_Data_by_EID ([string]$EmplId=»»)
{
if ($EmplId.length -eq 0) {Write-Verbose «Не передан табельный номер в качестве параметра»; return $false}
if($EmplId[0] -eq «э»){$firm = 9}elseif($EmplId[0] -eq «л»){$firm = 10}else{$firm = 1} #Для поиска сотрудника в списке конкретной компании
if(«э»,»л»,»в»–contains $EmplId[0]){$EmplId = $EmplId.SubString(1,$EmplId.length-1)} #Выделяем чисто табельный номер
#========= Подключаемся к серверу БД и читаем их базы данных данные о сотрудниках
#=== В результирующем наборе ОДИН сотрудник и его данные по основному месту работы — сотрудники — уникальны — повторяющихся нет (НО! — исключения есть — парочка)
$SQLCn = New-Object System.Data.SqlClient.SqlConnection( «Server=VSN_SRV4;Trusted_Connection=Yes;Database=Lite»)
$SQLCn.Open()
$SQLCMD = $SQLCn.CreateCommand()
$SQLCMD.CommandText = «SELECT distinct
(case
when people.id_firm = 9 then ‘э’+people.NUM_TAB
when people.id_firm = 10 then ‘л’+people.NUM_TAB
else ‘в’ +people.NUM_TAB end) as EmplID,    — ТАБЕЛЬНЫЙ НОМЕР \ ПОЧТОВЫЙ ЯЩИК
people.id_firm as FirmID,
card.Full_Name,            — ФИО \ ВЫВОДИМОЕ ИМЯ
card.Name,                — ФАМИЛИЯ \ ФАМИЛИЯ
card.Name_i,            — ИМЯ \ ИМЯ
LEFT(card.Name_i,1)+’.’+LEFT(card.Name_o,1)+’.’ as Initials,    — ИНИЦИАЛЫ \ ИНИЦИАЛЫ
card.date_birth as BirthDate,
card.typUdost as IdentityCardCode_ru,
card.passp_ser as IdentityCardSeries_ru,
card.passp_num as IdentityCardNumber_ru,
card.passp_date as IdentityCardIssueDate_ru,
card.passp_grant as IdentityCardIssueBy_ru,
card.addr_zip as ZipCode,            — ИНДЕКС \ ПОЧТОВЫЙ ИНДЕКС
card.addr_city as City,                — ГОРОД \ ГОРОД
card.addr_street as Street,            — УЛИЦА \ УЛИЦА
card.addr_house as House,            — ДОМ \ УЛИЦА + УЛИЦА
card.addr_block as Block,            — КОРПУС \ УЛИЦА + УЛИЦА + УЛИЦА
card.addr_flat as Flat,                — КВАРТИРА \ УЛИЦА + УЛИЦА + УЛИЦА + УЛИЦА
appointments.name_appoint as Title,    — ДОЛЖНОСТЬ \ ДОЛЖНОСТЬ
(case when structs.struct_lev =0  then structs_r.struct_name else structs.struct_name end) as Otdel,    — ПОДРАЗДЕЛЕНИЕ \ ОТДЕЛ
structs.tree_path,                    — ПОЛНЫЙ ПУТЬ \ ОПИСАНИЕ
setup.Short_name as Company,        — КОМПАНИЯ \ ОРГАНИЗАЦИЯ
people.in_date, — ДАТА ПРИЕМА НА РАБОТУ
people.out_date, — ДАТА УВОЛЬНЕНИЯ
(case when people.out_date = ‘2099-01-01′ then 1 else 0 end) as Works,    — РАБОТАЕТ \ ОТКЛЮЧИТЬ УЧЕТНУЮ ЗАПИСЬ
pr_group_value.string as EmplLevel    — УРОВЕНЬ \
FROM card
LEFT OUTER JOIN people (NOLOCK) on card.Auto_Card = people.Auto_Card
LEFT OUTER JOIN PR_CURRENT (NOLOCK) ON PEOPLE.PID = PR_CURRENT.pId
LEFT OUTER JOIN appointments (NOLOCK) on appointments.code_appoint = pr_current.code_appoint
JOIN structs on people.struct_code=structs.struct_code
JOIN structs structs_r on structs.struct_root=structs_r.struct_code
JOIN setup on setup.id_firm = people.id_firm
LEFT OUTER JOIN pr_group_value on pr_current.cell_item = pr_group_value.id_ref and pr_group_value.id_group = 147
WHERE PR_CURRENT.flag_last=’*’
AND people.id_firm = @firm — Выводим сотрудников только этой компании
AND people.NUM_TAB = @EmplId
AND out_date = (select max(out_date)
from people people1
where people.auto_card = people1.auto_card
and people1.id_firm in (1,9,10))
and pr_current.Work_Code = (select min(Work_Code)
from pr_current pr_current1, people people1
where pr_current1.pid = people1.pid
and pr_current1.auto_card=card.auto_card
and flag_last=’*’
and people1.out_date = (select max(out_date)
from people people2
where people1.auto_card = people2.auto_card
and people2.id_firm in (1,9,10)))»
$P = $SQLCMD.Parameters.AddwithValue(«@firm», $firm)
$P = $SQLCMD.Parameters.AddwithValue(«@EmplId», $EmplId)
$SQLResult = $SQLCMD.ExecuteReader() # В этой переменной результат запроса к базе БОСС кадровик

if($SQLResult.Read() -eq $false) {Write-Verbose «Нет данных для вывода по запрошенному табельному номеру ($EmplId)!»; return $false}
$out = @{EmplId=$SQLResult.Item(«EmplId»);
Full_Name=$SQLResult.Item(«Full_Name»);
Name=$SQLResult.Item(«Name»);
Name_i=$SQLResult.Item(«Name_i»);
Initials=$SQLResult.Item(«Initials»);
BirthDate=([datetime]$SQLResult.Item(«BirthDate»)).ToShortDateString();
ZipCode=$SQLResult.Item(«ZipCode»);
City=$SQLResult.Item(«City»);
Street=[string]$SQLResult.Item(«Street»)+», дом «+[string]$SQLResult.Item(«House»)+», блок «+[string]$SQLResult.Item(«Block»)+», квартира «+[string]$SQLResult.Item(«Flat»);
Title=$SQLResult.Item(«Title»);
Otdel=$SQLResult.Item(«Otdel»);
tree_path=$SQLResult.Item(«tree_path»);
Company=$SQLResult.Item(«Company»);
out_date=$SQLResult.Item(«out_date»);
Works=$SQLResult.Item(«Works»);
EmplLevel=$SQLResult.Item(«EmplLevel»);
}
$SQLCn.Close()
return $out
}
#===============================================================================
#===== НАЧАЛО ===========
«======================================================» >> $LogFile
#»ВНИМАНИЕ! ТЕСТИРОВАНИЕ! К ИСПОЛЕНИЮ НЕ ПРИНИМАТЬ! /И.Мамышев» >> $LogFile
«Синхронизация Active Directory с БОСС Кадровик» >> $LogFile
«Отчет о изменениях в Active Directory:» >> $LogFile
$LogFile >> $LogFile
$dt = Get-Date -Format «dd.MM.yyyy HH:mm:ss»
«Начало: «+$dt >> $LogFile
«——————————————————» >> $LogFile
#
$RecomText = «=====================================================================`n»
#$RecomText += «ВНИМАНИЕ! ТЕСТИРОВАНИЕ! К ИСПОЛНЕНИЮ НЕ ПРИНИМАТЬ! /И.Мамышев`n»
$RecomText += «РЕКОМЕНДАЦИИ по итогам Синхронизации Active Directory с БОСС Кадровик`n»
$RecomText += «Отчет о произведенных изменениях в Active Directory:`n» + $LogFile + «`n»
$RecomText += «Правила работы процесса синхронизации:`n» + $RulesFile + «`n»
$RecomText += «Начало: «+$dt + «`n»
$RecomText += «———————————————————————`n»
$RecomText += «Измените свойства учетных записей в соответствии с информацией ниже`n»
$RecomText += «, затем повторите или дождитесь следующей синхронизации.`n»
#========= Ищем пользователей в глобальном каталоге =======
cls
Write-Host ‘ОАО ПКК «Весна»‘
Write-Host «Синхронизация Active Directory и БОСС Кадровик»
Write-Host «Пожалуйста подождите..»
#$ADUsers=(Get-QADUser -UseGlobalCatalog -SizeLimit 0 -ErrorAction SilentlyContinue | where {($_.Name -eq «chn_bez1»)})
#$ADUsers=(Get-QADUser chn_bez1 -SizeLimit 0 -ErrorAction SilentlyContinue )
$ADUsers=( Get-QADUser -UseGlobalCatalog -SizeLimit 0 -ErrorAction SilentlyContinue | where {$_.DN -notlike «*Горизонт*»} |
where {$_.DN -notlike «*SRV*»} | where {$_.DN -notlike «*Календарь*»} | where {$_.DN -notlike «*SystemMailbox*»} |
where {$_.DisplayName -notlike «*пользователь*»} | where {$_.DisplayName -notlike «*робот*»} | where {$_.DisplayName -notlike «*robot*»} | where {$_.DN -notlike «*test*»} |
where {$_.DisplayName -notlike «*тест*»} | where {$_.DisplayName -notlike «*Служебная*»} | where {$_.DisplayName -notlike «*veritas*»} |
where {$_.DN -notlike «*ЧИН*»} | where {$_.DN -notlike «*Ветснаб*»} )
#=== Перебираем всех отобранных пользователей ActiveDirectory, пытаемся найти данные по ним в результатах запроса к БОССу
#&$ADUsers | ForEach-object {
$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 «Пожалуйста подождите.»; #Счетчик — процент выполнения
#=== Пробуем получить данные по сотруднику по ФИО ($_.DisplayName)
#=== Мы здесь даже тогда, когда поле ТАБЕЛЬНЫЙ НОМЕР заполнено, но данные в запросе к БОССу не нашли

#=== Если учетная запись пользователя в AD ВЫКЛЮЧЕНА, то выходим из цикла и идем к следующей итерации
if ([bool](Get-QADUser -Name $_.Name -UseGlobalCatalog -Disabled) -eq $True) { return; };

$Count2++
$b = Get-BOSS_Data_by_FN $_.DisplayName
Write-Verbose «$($_.PostOfficeBox) $($_.Name) $($_.DisplayName) $($_.Type) Счетчик: $Count2»

if ($b.Count -gt 0)
{#=== Информация из БОССа по ФИО получена
$dt = Get-Date -Format «dd.MM.yyyy HH:mm:ss»
if (($b[«EmplLevel»]).length -le 0) {$wp = «»} else {$wp = «Уровень » + $b[«EmplLevel»]}
$notes = «День рождения » + $b[«BirthDate»] + «. Синхронизировано с БОСС Кадровик » + $dt
if ($b[«Name»].Length -gt 64) {$b[«Name»]=$b[«Name»].SubString(0,64)}
if ($b[«Name_i»].Length -gt 64) {$b[«Name_i»]=$b[«Name_i»].SubString(0,64)}
if ($b[«Initials»].Length -gt 6) {$b[«Initials»]=$b[«Initials»].SubString(0,6)}
if ($b[«Title»].Length -gt 64) {$b[«Title»]=$b[«Title»].SubString(0,64)}
if ($b[«Otdel»].Length -gt 64) {$b[«Otdel»]=$b[«Otdel»].SubString(0,64)}
if ($b[«Company»].Length -gt 64) {$b[«Company»]=$b[«Company»].SubString(0,64)}
if ($b[«tree_path»].Length -gt 1024) {$b[«tree_path»]=$b[«tree_path»].SubString(0,1024)}
if ( ([string]$_.WebPage -ne [string]$wp)-or([string]$_.LastName -ne [string]$b[«Name»])-or([string]$_.FirstName -ne [string]$b[«Name_i»])-or([string]$_.Initials -ne [string]$b[«Initials»])-or([string]$_.PostOfficeBox -ne [string]$b[«EmplID»])-or([string]$_.Title -ne [string]$b[«Title»])-or([string]$_.Department -ne [string]$b[«Otdel»])-or([string]$_.Company -ne [string]$b[«Company»])-or([string]$_.Description -ne [string]$b[«tree_path»]) )
{#=== Если изменились свойства уч. записи — Записываем\обновляем свойства учетной записи ActiveDirectory
$result = Set-QADUser -Identity $_.DN -WebPage $wp -LastName $b[«Name»] -FirstName $b[«Name_i»] -Initials $b[«Initials»] -Notes $notes -PostOfficeBox $b[«EmplID»] -Title $b[«Title»] -Department $b[«Otdel»] -Company $b[«Company»] -Description $b[«tree_path»] #-WhatIf
«» >> $LogFile
«» + $Count + «. Обновлены свойства: » + $_.DN >> $LogFile;
#    «Было`t=>  `tWebPage:`t» + $_.WebPage + «,`tLastName:`t» + $_.LastName + «,`tFirstName:`t» + $_.FirstName + «,`tInitials:`t» + $_.Initials + «,`tNotes:`t» + $_.notes + «,`tPostalCode:`t» + $_.PostalCode + «,`tCity:`t» + $_.City + «,`tStreetAddress:`t» + $_.StreetAddress + «,`tPostOfficeBox:`t» + $_.PostOfficeBox + «,`tStateOrProvince:`t» + $_.StateOrProvince + «,`tTitle:`t» + $_.Title + «,`tDepartment:`t» + $_.Department + «,`tCompany:`t» + $_.Company + «,`tDescription:`t» + $_.Description >> $LogFile
#    «Стало`t=> `tWebPage:`t» + $wp + «,`tLastName:`t» + $b[«Name»] + «,`tFirstName:`t» + $b[«Name_i»] + «,`tInitials:`t» + $b[«Initials»] + «,`tNotes:`t» + $notes + «,`tPostalCode:`t» + $b[«ZipCode»] + «,`tCity:`t» + $b[«City»] + «,`tStreetAddress:`t» + $b[«Street»] + «,`tPostOfficeBox:`t» + $b[«EmplID»] + «,`tStateOrProvince:`t» + $b[«City»] + «,`tTitle:`t» + $b[«Title»] + «,`tDepartment:`t» + $b[«Otdel»] + «,`tCompany:`t» + $b[«Company»] + «,`tDescription:`t» + $b[«tree_path»] >> $LogFile
«Было`t=>  `tWebPage:`t» + $_.WebPage + «,`tLastName:`t» + $_.LastName + «,`tFirstName:`t» + $_.FirstName + «,`tInitials:`t» + $_.Initials + «,`tNotes:`t» + $_.notes + «,`tPostOfficeBox:`t» + $_.PostOfficeBox + «,`tTitle:`t» + $_.Title + «,`tDepartment:`t» + $_.Department + «,`tCompany:`t» + $_.Company + «,`tDescription:`t» + $_.Description >> $LogFile
«Стало`t=> `tWebPage:`t» + $wp + «,`tLastName:`t» + $b[«Name»] + «,`tFirstName:`t» + $b[«Name_i»] + «,`tInitials:`t» + $b[«Initials»] + «,`tNotes:`t» + $notes + «,`tPostOfficeBox:`t» + $b[«EmplID»] + «,`tTitle:`t» + $b[«Title»] + «,`tDepartment:`t» + $b[«Otdel»] + «,`tCompany:`t» + $b[«Company»] + «,`tDescription:`t» + $b[«tree_path»] >> $LogFile
if ($result -ne $False) {$Count++}
}
if([string]$b[«Works»] -eq «0»)
{$dtout = [datetime]$b[«out_date»];
#Обновим Выводимое имя пользователя
$NewDisplayName = $b[«Full_Name»] + » — уволен(а) с » + $dtout.ToShortDateString()
Set-QADUser -Identity $_.DN -DisplayName $NewDisplayName #-WhatIf
«» + $_.DN + » Сотрудник уволен. Выводимое Имя сменено на: » + $NewDisplayName >> $LogFile
#Отключим учетку пользователя, если она не отключена
if ([bool](Get-QADUser -Name $_.Name -UseGlobalCatalog -Disabled) -eq $False) {
Get-QADUser $_.DN | ForEach-Object { $LL = ([datetime]$_.LastLogon).ToString(); if ( $LL.Length -eq 0 ) { $LL = «Нет данных» } };
$RecomTextDSBL += «» + $Count4 + «. » + $b[«Full_Name»] + » уволен(а) с » + $dtout.ToShortDateString() + «`t» + $_.DN + «`tLastLogon: » + $LL + «`n»;
#Disable-QADUser $_.DN -WhatIf ; $RecomTextDSBL += «Действие: Отключена учетная запись $accountDN`n»
$Count4++
} #else{«Уже отключена»}
}
}elseif($b -eq 100){
#=== Информация в БОССе ЗАПРАШИВАЛАСЬ по ФИО и НЕ БЫЛА ПОЛУЧЕНА, готовим инфу для лога рекомендаций
Get-QADUser $_.DN | ForEach-Object { $LL = ([datetime]$_.LastLogon).ToString(); if ( $LL.Length -eq 0 ) { $LL = «Нет данных» } };
$BOSS_Data_by_FN_recomend += «`t» + $_.DN + «`tLastLogon: » + $LL + «`n» + $_.Description + «`n»;
}

IF ($_.PostOfficeBox.Length -gt 0)
{
#Служебная#    Set-QADUser -Identity $_.DN -PostOfficeBox «» #Обнулить всем поле PostOfficeBox (то, куда мы пишем табельный номер из БОССа)
#=== Если заполнено поле табельный номер, то пробуем получить данные по сотруднику по табельному номеру
$b2 = Get-BOSS_Data_by_EID $_.PostOfficeBox
Write-Verbose «$($_.PostOfficeBox) $($_.Name) $($_.DisplayName)»
if( ($b2.Count -gt 0)-and([string]$b2[«Name»] -ne [string]$_.LastName)-and([string]$b2[«Name_i»] -eq [string]$_.FirstName)-and([string]$b2[«Initials»] -eq [string]$_.Initials) )
{#=== Если нашли данные в БОССе по табельному номеру, ИМЯ и ИНИЦИАЛЫ совпадают , а ФАМИЛИЯ в учетке АД НЕ совпадает с ФАМИЛИЕЙ из БОССа, то
#=== Готовим рекомендацию — СМЕНИЛАСЬ ФАМИЛИЯ ПОЛЬЗОВАТЕЛЯ —
$RecomTextLN += «» + $Count3 + «. Изменилась ФАМИЛИЯ (» + $_.PostOfficeBox +»): » + $_.DN + «`t» + $_.DisplayName + «`n»
$RecomTextLN += «Было (AD)`t`t=>  `t» + $_.LastName + «`t» + $_.FirstName + «`t» + $_.Initials + «`n»
$RecomTextLN += «Стало (БОСС)`t=> `t» + $b2[«Name»] + «`t» + $b2[«Name_i»] + «`t» + $b2[«Initials»] + «`n»
$RecomTextLN += «===`n»
$Count3++
#Return #=== Переходим к следующему циклу ForEach-object
}
if( $b2 -eq $False )
{
#=== Записанный в учетке Табельный номер не найден в БОССе, удалим Таб.номер как неверный
Write-Verbose «Табельный номер, указанный в AD не существует в БОССе!»
Set-QADUser -Identity $_.DN -PostOfficeBox «» #-WhatIf
}
if( ($b2.Count -gt 0)-and($b2[«Name»] -eq $_.LastName)-and($b2[«Name_i»] -eq $_.FirstName) )
{
#=== Записанный в учетке Табельный номер  найден в БОССе
Write-Verbose «Табельный номер, указанный в AD НАЙДЕН в БОССе! ФИ соответствуют!!!»
}
}
}#================================================================
«Обновлено » + ($Count — 1) + » учетных записей пользователей Active Directory» >> $LogFile
$dt = Get-Date -Format «dd.MM.yyyy HH:mm:ss»
«Конец: «+$dt >> $LogFile
#================================================================

if ($RecomTextLN.Length -gt 0){
$RecomText += «———————————————————————`n»
$RecomText += «Список сотрудников, у которых изменилась фамилия.`n»
$RecomText += «Если рекомендации в части ИЗМЕНЕНИЯ ФАМИЛИИ неверны — удалите содержимое поля ‘Адрес\Почтовый ящик’ свойств учетной записи пользователя.`n»
$RecomText += $RecomTextLN
}

if ($RecomTextDSBL.Length -gt 0){
$RecomText += «=====================================================================`n»
$RecomText += «Следующие сотрудники уволены, — рекомендуется отключить уч. записи:`n»
$RecomText += $RecomTextDSBL
}

$RecomText += «=====================================================================`n»
$RecomText += «Следующие ФИО не были обнаружены при запросе к БОСС Кадровик, возможно их необходимо откорректировать:`n»
$RecomText += $script:BOSS_Data_by_FN_recomend
$RecomText += «Конец: «+$dt + «`n»
Write-Progress -Activity «Working…» -Completed -Status «All done.» #Отображение индикатора выполнения завершим. Пропадет с экрана.
#.\Send-SmtpMail.ps1 -to $SmtpTo -subject «Синхронизация Active Directory и БОСС-Кадровик. РЕКОМЕНДАЦИИ АДМИНИСТРАТОРУ.» -body $RecomText
#===============================================================================
#=== Отправка EMail
#===============================================================================
Write-Verbose «Создаем объекты SmtpClient и MailMessage»
$SmtpClient = New-Object System.Net.Mail.SmtpClient
$Message = New-Object System.Net.Mail.MailMessage
Write-Verbose «Устанавливаем свойства этих объектов»
$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)
Write-Verbose «Удаляем объекты»
$Message.Dispose()
#===============================================================================
Write-Host «Синхронизация завершена.»
Write-Host «Отчет о произведенных изменениях в Active Directory: $LogFile»
Write-Host «Рекомендации по ручной корректировке базы данных AD высланы на $SmtpTo»
#===== КОНЕЦ ===========

Рубрики:PowerShell

Программное обеспечение и драйвера для оборудования Hewlett Packard

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

Облегчить Вашу нелегкую работу поможет "HP SoftPaq Download Manager".

 HP SoftPaq DM

Рубрики:Разное

Оповещение о наличии длительной блокировки на SQL Server

Встал как то вопрос о том чтобы последить за длительными блокировками на SQL Server 2000.
Такие блокировки могут быть как от действительно длительных и очень длительных (возможно "кривых" запросов) процессов, так и от "зависших" пользовательских процессов.
Можно например запустив Enterprise Manager посмотреть Current Activity и увидеть очередь возникших блокировок, иногда EM задумывается на этом процессе неприемлемо долго.
Можно в Query Analyzer выполнить хранимую процедуру sp_who2 и также определить очередь и блокирующий(ие) процессы.
А как сделать так, чтобы ничего не делать? 🙂 — правильно — автоматизировать.

Родился нижележащий скрипт.
Создаем на его основе джоб, периодически выполняем, периодически получаем уведомление на email оповещение о длительной блокировке.

Не продакшен версия конечно, но кому пригодится — черкните.
===================================================================
—Оповещение о наличии длительной блокировки на сервере
— Отправляем email в случае обнаружения длительной блокировки

SET NOCOUNT ON
— Создадим переменные
DECLARE @LimitWaitTime int,
@MaxWaitTime int,
@SPID smallint,
@subject_str varchar(255),
@message_str varchar(255),
@separator_str varchar(1),
@email varchar(128)

— Определим значения переменных
SET @LimitWaitTime=300000 — Максимальное время в милисекундах ожидания в очереди блокировок, при превышении которого отсылаем email (типа признак ДЛИТЕЛЬНОЙ блокировки)
SET @separator_str=CHAR(9) — Символ табуляции
SET @email = ‘events@vesna.ru’

— создаем временную таблицу для хранения информации от sp_who2
IF (object_id(‘tempdb..#t1’) IS not null) DROP TABLE #t1
IF (object_id(‘tempdb..##t2’) IS not null) DROP TABLE ##t2
CREATE TABLE #t1 (SPID smallint, Status varchar(30), Login varchar(128), HostName varchar(128), BlkBy varchar(5), DBName varchar(128), Command varchar(16), CPUTime int, DiskIO bigint, LastBatch varchar(128), ProgramName varchar(128), SPID2 smallint);

— Выполняем и сохраняем результат sp_who2
INSERT INTO #t1 EXECUTE (‘sp_who2’)

— Дополняем результат sp_who2 данными о WaitTime
— Некоторые длинные поля немного урезаем SUBSTRING-ом для приемлемого вида
SELECT
tbl1.SPID,
SUBSTRING(tbl1.Status,1,15) AS Status,
SUBSTRING(Login,1,25) AS Login,
SUBSTRING(tbl1.HostName,1,15) AS HostName,
BlkBy, SUBSTRING(DBName,1,15) AS DBName,
SUBSTRING(Command,1,35) AS Command,
CPUTime,
DiskIO,
SUBSTRING(LastBatch,1,15) AS LastBatch,
SUBSTRING(ProgramName,1,60) AS ProgramName,
WaitTime
INTO ##t2
FROM #t1 AS tbl1 LEFT OUTER JOIN master..sysprocesses AS tbl2 WITH (NOLOCK) ON tbl1.SPID = tbl2.SPID
WHERE tbl1.SPID IN (SELECT BlkBy FROM #t1 WHERE BlkBy not like ‘%.%’) OR BlkBy not like ‘%.%’

— Удаляем временную таблицу
IF (object_id(‘tempdb..#t1’) IS not null) DROP TABLE #t1

— Максимальное время ожидания в очереди блокировок
SELECT @MaxWaitTime=MAX(WaitTime) FROM ##t2
SELECT @MaxWaitTime AS [Максимальное время ожидания (мсек)]

— Блокирующий процесс
SELECT @SPID=SPID FROM ##t2 WHERE BlkBy like ‘%.%’
SELECT @SPID AS [Блокирующий SPID]
SELECT DISTINCT * FROM ##t2

— Отправляем email оповещение об обнаружении ДЛИТЕЛЬНОЙ блокировки
IF (@MaxWaitTime>@LimitWaitTime) BEGIN

— Подготовим текст сообщения
SET @subject_str = ‘SQL Server ‘+@@SERVERNAME+’: Длительная блокировка! (SPID:’+convert(varchar(5),@SPID)+’)’
SET @message_str = ‘Обнаружена длительная блокировка!

Сервер ‘+@@SERVERNAME+’
Идентификатор процесса (SPID) = ‘+convert(varchar(5),@SPID)+’,
Максимальное время ожидания в очереди блокировок = ‘+convert(varchar(5),@MaxWaitTime/60000)+’ мин.,

Примите меры!’

— Отправляем email
EXEC xp_sendmail @recipients = @email,
@query = ‘SELECT * FROM ##t2’,
@subject = @subject_str,
@message = @message_str,
@attachments = ‘sp_who2.txt’,
@attach_results = ‘TRUE’,
@width = 1024,
@separator = @separator_str
—,@no_header= ‘TRUE’
END

— Удаляем временную таблицу
IF (object_id(‘tempdb..##t2’) IS not null) DROP TABLE ##t2
===================================================================

Применимо:
MS SQL Server 2000

Рубрики:SQL Server