Ай Лайн
Связаться
Отдел продаж +7 (812) 385-74-84
Время работы пн-пт с 9:00 до 18:00

Интеграция 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 вебхука:

  1. Метод im.user.get - для получения уже обрезанных аватарок по id пользователя в B24

Метод im.user.get - для получения уже обрезанных аватарок по id пользователя в B24

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

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

  2. Записываем ссылки и api токены, их надо будет указать в скриптах в дальнейшем

1.4. Добавление пользовательского атрибута для пользователей в AD

  1. Заходим на любой DC, открываем MMC через RUN, добавляем оснастку «Схема Active Directory»
  2. Открываем и создаём атрибут в разделе Атрибуты (как на скриншоте)
    Открываем и создаём атрибут в разделе Атрибуты (как на скриншоте)
bitrixUserId
ID пользователя в Bitrix24
bitrix_user_id
1.2.840.113556.1.4.700*
Целое
  1. Переходим в «Классы» и находим класс «user» - открываем его свойства.

  2. Во вкладке Атрибуты, добавляем наш атрибут bitrixUserId.

    Во вкладке Атрибуты, добавляем наш атрибут 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 профили

  1. Создайте GPO, которая будет применять скрипт установки аватарок.
  2. Включите параметр "Run PowerShell Scripts" для логина пользователя.
  3. Укажите путь к скрипту set_avatar_in_windows_profil_gpo.ps1.
  4. Примените GPO для нужных пользователей или компьютеров в AD.

Для более подробных шагов смотрите статью: Как установить аватары пользователей через GPO.

4. Настройка планировщика заданий для скрипта run_all_scripts.ps1

4.1. Открытие планировщика заданий

  1. Откройте Task Scheduler (taskschd.msc).
  2. В левой панели выберите Task Scheduler Library.

4.2. Создание нового задания

  1. Нажмите Create Task в правой панели.
  2. Вкладка General: введите имя задачи, например, Run Bitrix Integration Scripts.
  3. Выберите Run whether user is logged on or not и Run with highest privileges.

4.3. Настройка триггера

  1. Перейдите на вкладку Triggers и нажмите New.
  2. В поле Begin the task выберите On a schedule.
  3. Настройте расписание выполнения скриптов (например, ежедневно или по мере необходимости).

4.4. Настройка действия

  1. Перейдите на вкладку Actions и нажмите New.
  2. В поле Action выберите Start a program.
  3. В поле Program/script укажите путь к PowerShell, например: powershell.exe.
  4. В поле Add arguments укажите полный путь к скрипту run_all_scripts.ps1, например: C:\Scripts\run_all_scripts.ps1.
  5. Нажмите OK.
  6. Нажмите OK, чтобы сохранить задачу.

Теперь ваш процесс интеграции с Bitrix24 и Active Directory полностью настроен!

Теперь ваш процесс интеграции с Bitrix24 и Active Directory полностью настроен!

Теперь ваш процесс интеграции с Bitrix24 и Active Directory полностью настроен!

Оставить заявку