Интеграция Bitrix24 и Active Directory (аватарки пользователей)
1. Подготовка файлов и скриптов и api
1.1. Подготовьте скрипты
Вы получите следующие скрипты, которые нужно будет использовать для интеграции:
- get_user_info.ps1 — сбор информации о пользователях из Active Directory.
- get_bitrix_id.ps1 — получение и обновление Bitrix ID пользователей.
- get_avatar&set_users_bitrix.ps1 — скачивание аватарок и обновление их в Active Directory.
- run_all_scripts.ps1 — выполнение всех предыдущих скриптов по очереди.
- set_avatar_in_windows_profil_gpo.ps1 — установка аватарок на профили Windows через GPO.
1.2. Папки и директории
Создайте папки для хранения скриптов, логов и изображений:
C:\Scripts\— для хранения скриптов.C:\Scripts\logs\— для логов.C:\Scripts\avatar\— для скачанных аватарок.
1.3. Настройка api bitrix24
Авторизовываемся в bitrix24, в левом меню выбираем Приложения → Разработчикам → Другое → Входящий вебхук.
Нам потребуется создать 2 вебхука:
- Метод im.user.get - для получения уже обрезанных аватарок по id пользователя в B24

-
Метод user.search - для сопоставления пользователей AD и B24 по ФИО и получения ID пользователя B24.

-
Записываем ссылки и api токены, их надо будет указать в скриптах в дальнейшем
1.4. Добавление пользовательского атрибута для пользователей в AD
- Заходим на любой DC, открываем MMC через RUN, добавляем оснастку «Схема Active Directory»
- Открываем и создаём атрибут в разделе Атрибуты (как на скриншоте)

bitrixUserId
ID пользователя в Bitrix24
bitrix_user_id
1.2.840.113556.1.4.700*
Целое
-
Переходим в «Классы» и находим класс «user» - открываем его свойства.
-
Во вкладке Атрибуты, добавляем наш атрибут bitrixUserId.

2. Настройка скриптов
2.1. Скрипт get_user_info.ps1
Скрипт собирает информацию о пользователях из Active Directory, включая атрибуты GivenName, Surname, SamAccountName и bitrixUserId. Пользователи, у которых не заполнен bitrixUserId, будут добавлены в CSV-файл.
Import-Module ActiveDirectory
$users = Get-ADUser -Filter * -Properties GivenName, Surname, SamAccountName, bitrixUserId
$csvData = @()
foreach ($user in $users) {
if ($user.GivenName -and $user.Surname -and -not $user.bitrixUserId) {
$csvData += [PSCustomObject]@{
FirstName = $user.GivenName
LastName = $user.Surname
SamAccountName = $user.SamAccountName
bitrixUserId = $user.bitrixUserId
}
}
}
$csvData | Export-Csv -Path "C:\Scripts\users_info.csv" -NoTypeInformation -Encoding UTF8
2.2. Скрипт get_bitrix_id.ps1
Этот скрипт использует API Bitrix24 для получения ID пользователей по имени и фамилии, затем обновляет соответствующие поля в Active Directory.
# Константы
$secret = "" # Указать свой сикрет
$csvInputPath = "C:\Scripts\users_info.csv"
$csvOutputPath = "C:\Scripts\users_info_updated.csv"
$baseUrl = "https://example.net/rest/1231/$secret/user.search.json?NAME=" # Указать свою ссылку
# Создание папки для логов
$logDir = "C:\Scripts\logs"
if (-not (Test-Path $logDir)) { New-Item -Path $logDir -ItemType Directory -Force }
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$logPath = "$logDir\get_bitrix_id_log_$timestamp.txt"
# Ротация логов (оставляем 5 последних файлов)
$logFiles = Get-ChildItem -Path $logDir -Filter "get_bitrix_id_log_*.txt" | Sort-Object LastWriteTime -Descending
if ($logFiles.Count -ge 5) {
$logFiles | Select-Object -Skip 4 | Remove-Item -Force
}
# Счетчики
$successApiCount = 0
$failureApiCount = 0
$successAdCount = 0
$failureAdCount = 0
Add-Type -AssemblyName System.Web
function Encode-Url {
param ([string]$String)
return [System.Web.HttpUtility]::UrlEncode($String)
}
function Invoke-RestMethodWithRetry {
param ([string]$Uri, [int]$MaxRetries = 3, [int]$RetryDelay = 5)
$retryCount = 0
do {
try {
$msg = "$(Get-Date) - Запрос: $Uri"
Write-Host $msg
Add-Content -Path $logPath -Value $msg
$response = Invoke-RestMethod -Uri $Uri -Method Get
return $response
}
catch {
$retryCount++
$msg = "$(Get-Date) - Ошибка запроса: $_"
Write-Host $msg
Add-Content -Path $logPath -Value $msg
if ($retryCount -ge $MaxRetries) {
return $null
}
Start-Sleep -Seconds $RetryDelay
}
} while ($true)
}
# Чтение входного CSV
$users = Import-Csv -Path $csvInputPath
if ($users.Count -eq 0) {
$msg = "$(Get-Date) - Ошибка: CSV пустой"
Write-Host $msg
Add-Content -Path $logPath -Value $msg
exit 1
}
$updatedUsers = @()
foreach ($user in $users) {
if (-not $user.FirstName -or -not $user.LastName) {
continue
}
$firstName = Encode-Url -String $user.FirstName
$lastName = Encode-Url -String $user.LastName
$uri = "$baseUrl$firstName&LAST_NAME=$lastName"
$response = Invoke-RestMethodWithRetry -Uri $uri
if ($response -and $response.result -and $response.result.Count -gt 0) {
$bitrixUserId = $response.result[0].id
$successApiCount++
} else {
$bitrixUserId = "Не найден"
$failureApiCount++
}
$updatedUsers += [PSCustomObject]@{
FirstName = $user.FirstName
LastName = $user.LastName
SamAccountName = $user.SamAccountName
bitrixUserId = $bitrixUserId
}
}
$updatedUsers | Export-Csv -Path $csvOutputPath -NoTypeInformation -Encoding UTF8
# Обновление данных в Active Directory
foreach ($user in $updatedUsers) {
if (-not $user.SamAccountName -or $user.bitrixUserId -eq "Не найден") {
continue
}
try {
$adUser = Get-ADUser -Identity $user.SamAccountName -ErrorAction Stop
Set-ADUser -Identity $user.SamAccountName -Replace @{bitrixUserId = $user.bitrixUserId}
$successAdCount++
} catch {
$msg = "$(Get-Date) - Ошибка обновления AD: $_"
Write-Host $msg
Add-Content -Path $logPath -Value $msg
$failureAdCount++
}
}
# Итоговый отчет
$summary = @"
$(Get-Date) - Итог:
API:
Успешно найдено ID: $successApiCount
Не найдено ID: $failureApiCount
Active Directory:
Успешно обновлено: $successAdCount
Ошибок обновления: $failureAdCount
Экспорт завершен в $csvOutputPath
"@
Write-Host $summary
Add-Content -Path $logPath -Value $summary
2.3. Скрипт get_avatar&set_users_bitrix.ps1
Этот скрипт скачивает аватарки пользователей из Bitrix и устанавливает их в Active Directory.
Add-Type -AssemblyName System.Drawing
# Параметры
$secret = "" #Указать свой сикрет
$savePath = "C:\Scripts\avatar"
$logDir = "C:\Scripts\logs"
$maxSizeBytes = 100 * 1024 # 100 KB
# Создание папки для логов
if (-not (Test-Path $logDir)) { New-Item -Path $logDir -ItemType Directory -Force }
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$logPath = "$logDir\get_avatar&set_users_bitrix_log_$timestamp.txt"
# Ротация логов (оставляем 5 последних файлов)
$logFiles = Get-ChildItem -Path $logDir -Filter "get_avatar&set_users_bitrix_log_*.txt" | Sort-Object LastWriteTime -Descending
if ($logFiles.Count -ge 5) {
$logFiles | Select-Object -Skip 4 | Remove-Item -Force
}
# Счетчики
$successCount = 0
$failureCount = 0
$resizeSuccessCount = 0
$resizeFailureCount = 0
# Получаем всех пользователей и необходимые атрибуты из AD
$users = Get-ADUser -Filter * -Properties bitrixUserId, sAMAccountName
foreach ($user in $users) {
$bitrixId = $user.bitrixUserId
if ([string]::IsNullOrEmpty($bitrixId)) {
$msg = "$(Get-Date) - Пропущен: bitrixUserId пустой для $($user.sAMAccountName)"
Write-Host $msg
Add-Content -Path $logPath -Value $msg
continue
}
$fileName = $user.sAMAccountName + ".jpg"
$apiUrl = "https://example.net/rest/1231/$secret/im.user.get.json?ID=$bitrixId&AVATAR_HR=N" # Указать свою ссылку
try {
$response = Invoke-RestMethod -Uri $apiUrl -ErrorAction Stop
$avatarUrl = $response.result.avatar
if ([string]::IsNullOrEmpty($avatarUrl)) {
throw "Аватар отсутствует в ответе API"
}
$fullPath = Join-Path -Path $savePath -ChildPath $fileName
Invoke-WebRequest -Uri $avatarUrl -OutFile $fullPath -ErrorAction Stop
$msg = "$(Get-Date) - Успешно сохранен: $fullPath"
Write-Host $msg
Add-Content -Path $logPath -Value $msg
$successCount++
} catch {
$msg = "$(Get-Date) - Ошибка скачивания аватара для $($user.sAMAccountName): $_"
Write-Host $msg
Add-Content -Path $logPath -Value $msg
$failureCount++
}
}
# Обработка изображений
$files = Get-ChildItem -Path $savePath -Filter *.jpg
foreach ($file in $files) {
$sAMAccountName = [System.IO.Path]::GetFileNameWithoutExtension($file.Name)
$filePath = $file.FullName
$image = [System.Drawing.Image]::FromFile($filePath)
try {
if ($file.Length -ge $maxSizeBytes) {
$quality = 90
do {
$jpegEncoder = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders() | Where-Object { $_.MimeType -eq 'image/jpeg' }
$encoderParams = New-Object System.Drawing.Imaging.EncoderParameters(1)
$encoderParams.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter([System.Drawing.Imaging.Encoder]::Quality, $quality)
$tempFile = "$($file.DirectoryName)\temp_$($file.Name)"
$image.Save($tempFile, $jpegEncoder, $encoderParams)
$tempFileSize = (Get-Item $tempFile).Length
if ($tempFileSize -lt $maxSizeBytes) {
$photoBytes = [byte[]](Get-Content $tempFile -Encoding Byte)
Remove-Item $tempFile
$resizeSuccessCount++
break
} else {
$quality -= 10
if ($quality -lt 10) {
$msg = "$(Get-Date) - Ошибка: невозможно уменьшить изображение для $sAMAccountName до 100 КБ."
Write-Host $msg
Add-Content -Path $logPath -Value $msg
Remove-Item $tempFile
$resizeFailureCount++
continue
}
}
} while ($true)
} else {
$photoBytes = [byte[]](Get-Content $filePath -Encoding Byte)
}
Set-ADUser -Identity $sAMAccountName -Replace @{thumbnailPhoto=$photoBytes}
$msg = "$(Get-Date) - Аватар установлен для $sAMAccountName"
Write-Host $msg
Add-Content -Path $logPath -Value $msg
} catch {
$msg = "$(Get-Date) - Ошибка обработки изображения для $sAMAccountName"
Write-Host $msg
Add-Content -Path $logPath -Value $msg
}
}
# Итоговый отчет
$summary = @"
$(Get-Date) - Итог:
Успешно скачано: $successCount
Ошибок скачивания: $failureCount
Успешно сжато: $resizeSuccessCount
Ошибок сжатия: $resizeFailureCount
"@
Write-Host $summary
Add-Content -Path $logPath -Value $summary
2.4. Скрипт run_all_scripts.ps1
Этот скрипт используется для выполнения всех предыдущих скриптов по очереди.
$logDir = "C:\Scripts\logs"
if (-not (Test-Path $logDir)) { New-Item -Path $logDir -ItemType Directory -Force }
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$logPath = "$logDir\run_all_scripts_log_$timestamp.txt"
function Run-Script {
param (
[string]$scriptPath
)
$msg = "$(Get-Date) - Запуск скрипта: $scriptPath"
Write-Host $msg
Add-Content -Path $logPath -Value $msg
try {
& $scriptPath
$msg = "$(Get-Date) - Успешно выполнен: $scriptPath"
} catch {
$msg = "$(Get-Date) - Ошибка в $scriptPath"
}
Write-Host $msg
Add-Content -Path $logPath -Value $msg
}
# Запуск скриптов
Run-Script "C:\Scripts\get_user_info.ps1"
Run-Script "C:\Scripts\get_bitrix_id.ps1"
Run-Script "C:\Scripts\get_avatar&set_users_bitrix.ps1"
$msg = "$(Get-Date) - Все скрипты выполнены."
Write-Host $msg
Add-Content -Path $logPath -Value $msg
2.5. Скрипт set_avatar_in_windows_profil_gpo.ps1
Этот скрипт используется для установки аватарок на профили Windows через GPO.
Function ResizeImage {
Param (
[Parameter(Mandatory = $True, HelpMessage = "image in byte")]
[ValidateNotNull()]
$imageSource,
[Parameter(Mandatory = $true, HelpMessage = "Параметр размер должен быть от 16 до 1000")]
[ValidateRange(16, 1000)]
$canvasSize,
[Parameter(Mandatory = $true, HelpMessage = "Параметр качества должен быть от 1 до 100")]
[ValidateRange(1, 100)]
$ImgQuality = 100
)
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$imageBytes = [byte[]]$imageSource
$ms = New-Object IO.MemoryStream($imageBytes, 0, $imageBytes.Length)
$ms.Write($imageBytes, 0, $imageBytes.Length);
$bmp = [System.Drawing.Image]::FromStream($ms, $true)
# Размер картинки после конвертации
$canvasWidth = $canvasSize
$canvasHeight = $canvasSize
# Задание качества картинки
$myEncoder = [System.Drawing.Imaging.Encoder]::Quality
$encoderParams = New-Object System.Drawing.Imaging.EncoderParameters(1)
$encoderParams.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter($myEncoder, $ImgQuality)
#Получаем тип картинки
$myImageCodecInfo = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders() | Where-Object { $_.MimeType -eq 'image/jpeg' }
# Высчитывание кратности
$ratioX = $canvasWidth / $bmp.Width;
$ratioY = $canvasHeight / $bmp.Height;
$ratio = $ratioY
if ($ratioX -le $ratioY) {
$ratio = $ratioX
}
# Создание пустой картинки
$newWidth = [int] ($bmp.Width * $ratio)
$newHeight = [int] ($bmp.Height * $ratio)
$bmpResized = New-Object System.Drawing.Bitmap($newWidth, $newHeight)
$graph = [System.Drawing.Graphics]::FromImage($bmpResized)
$graph.Clear([System.Drawing.Color]::White)
$graph.DrawImage($bmp, 0, 0 , $newWidth, $newHeight)
# Создание пустого потока
$ms = New-Object IO.MemoryStream
$bmpResized.Save($ms, $myImageCodecInfo, $($encoderParams))
# уборка
$bmpResized.Dispose()
$bmp.Dispose()
return $ms.ToArray()
}
$ADUserInfo = ([ADSISearcher]"(&(objectCategory=User)(SAMAccountName=$env:username))").FindOne().Properties
$ADUserInfo_sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value
If ($ADUserInfo.thumbnailphoto) {
$img_sizes = @(32, 40, 48, 96, 192, 200, 240, 448)
$img_base = "C:\Users\Public\AccountPictures"
$reg_key = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AccountPicture\Users\$ADUserInfo_sid"
If ((Test-Path -Path $reg_key) -eq $false) { New-Item -Path $reg_key } { write-verbose "Reg key exist [$reg_key]" }
Try {
ForEach ($size in $img_sizes) {
$dir = $img_base + "\" + $ADUserInfo_sid
If ((Test-Path -Path $dir) -eq $false) { $(New-Item -ItemType directory -Path $dir).Attributes = "Hidden" }
$file_name = "Image$($size).jpg"
$path = $dir + "\" + $file_name
Write-Verbose " Crete file: [$file_name]"
try {
ResizeImage -imageSource $($ADUserInfo.thumbnailphoto) -canvasSize $size -ImgQuality 100 | Set-Content -Path $path -Encoding Byte -Force -ErrorAction Stop
Write-Verbose " File saved: [$file_name]"
}
catch {
If (Test-Path -Path $path) {
Write-Warning "File exist [$path]"
}
else {
Write-Warning "File not exist [$path]"
}
}
$name = "Image$size"
try {
$null = New-ItemProperty -Path $reg_key -Name $name -Value $path -Force -ErrorAction Stop
}
catch {
Write-Warning "Reg key edit error [$reg_key] [$name]"
}
}
}
Catch {
Write-Error "Check permissions to files or registry."
}
}
3. Настройка GPO для установки аватарок на Windows профили
- Создайте GPO, которая будет применять скрипт установки аватарок.
- Включите параметр "Run PowerShell Scripts" для логина пользователя.
- Укажите путь к скрипту
set_avatar_in_windows_profil_gpo.ps1. - Примените GPO для нужных пользователей или компьютеров в AD.
Для более подробных шагов смотрите статью: Как установить аватары пользователей через GPO.
4. Настройка планировщика заданий для скрипта run_all_scripts.ps1
4.1. Открытие планировщика заданий
- Откройте
Task Scheduler(taskschd.msc). - В левой панели выберите
Task Scheduler Library.
4.2. Создание нового задания
- Нажмите Create Task в правой панели.
- Вкладка General: введите имя задачи, например,
Run Bitrix Integration Scripts. - Выберите Run whether user is logged on or not и Run with highest privileges.
4.3. Настройка триггера
- Перейдите на вкладку Triggers и нажмите New.
- В поле Begin the task выберите On a schedule.
- Настройте расписание выполнения скриптов (например, ежедневно или по мере необходимости).
4.4. Настройка действия
- Перейдите на вкладку Actions и нажмите New.
- В поле Action выберите Start a program.
- В поле Program/script укажите путь к PowerShell, например:
powershell.exe. - В поле Add arguments укажите полный путь к скрипту
run_all_scripts.ps1, например:C:\Scripts\run_all_scripts.ps1. - Нажмите OK.
- Нажмите OK, чтобы сохранить задачу.
Теперь ваш процесс интеграции с Bitrix24 и Active Directory полностью настроен!

