Меню Закрыть

Телефонный справочник на Sharepoint (WSS) с синхронизацией из AD

Давным-давно, когда я только познакомился с Sharepoint, мне хотелось сделать телефонный справочник, который бы брал данные из AD, да еще и синхронизировал бы все изменения. Появился PowerShell, и реализация моей хотелки стала проще-простого!
Представляю Вашему вниманию решение, которое берет список пользователей и контактов из базы AD и выкладывает в виде списка Sharepoint (WSS). Список является немного модифицированным стандартным шаблоном “Контакты” для лучшего визуального отображения контактов, удобного поиска и добавления фото сотрудников. Справочник  постоянно находится в актуальном состоянии благодаря ежедневной синхронизации.

Итак, у нас имеется стандартная установка Windows Sharepoint Services 3.0 с именем wss-portal, узел phone, созданный из стандартного шаблона “Контакты”, а также немного модифицированный список на этом узле с именем “Контакты”.

Скрипт размещается на сервере Sharepoint и запускается под именем пользователя, имеющего права на чтение каталога AD и права на запись в узел phone.

Так как создавался данный скрипт до выхода в свет PowerShell 2.0, для получения списка пользователей и контактов из AD я использовал командлеты ActiveRoles Management Shell for Active Directory от компании Quest Software. При желании можно переделать на PoSh 2.0 без QAD.

Механизм работы скрипта:

  • Подключение к списку WSS
  • Очистка списка
  • Подключение к доменному контроллеру dc.domain.corp (измените на свой)
  • Получение списка пользователей домена, чьи имена начинаются с русских букв
  • Получение атрибутов этих пользователей: Отображаемое имя, должность, организация, отдел, кабинет, почтовый индекс, город, почтовый адрес, телефонный номер, пейджер (позже расскажу для чего), мобильный телефон, адрес электронной почты, web страница (я использую этот атрибут для размещения фото) и атрибут блокировки учетной записи
  • Если атрибут пользователя “Пейджер” не содержит символ “@” и если учетная запись не отключена, все полученные учетки выкладываются в список Sharepoint в соответствующие столбцы
  • если атрибут пользователя “WEB страница”  пуст, то в поле списка записывается значение “http://portal.domain.corp/phone/photo/nophoto.jpg”, в противном случае прописывается путь к фото сотрудника
  • далее последние пункты повторяются для контактов домена

Сам скрипт:

 

Как можно увидеть из содержания скрипта, я загрузил все фотографии пользователей в папку http://portal.domain.corp/phone/photo/ и добавил путь к фото в поле атрибута пользователя “WEB страница”. В список WSS в свою очередь добавлено поле “Фотография пользователя”.

Для удобства прилагаю шаблон списка “Контакты” и сам скрипт:

Шаблон списка контактов
Шаблон списка контактов
Скрипт на PowerShell
Скрипт на PowerShell

20 Comments

  1. Рустам

    конструкция
    $splist.get_items() | % { $_.Delete() } #Полностью очищаем список
    не работает, пишет:

    Произошла ошибка при перечислении элементов коллекции: Collection was modified; enumeration operation may not execute..
    строка:1 знак:1
    + <<<< $splist.get_items() | % { $_.Delete() } #Полностью очищаем список
    + CategoryInfo : InvalidOperation: (Microsoft.Share…on+SPEnumerator:SPEnumerator) [], RuntimeException
    + FullyQualifiedErrorId : BadEnumeration

    —————
    (Windows Server 2008 r2, PowerShell 2.0, Office SharePoint Server 2010)

  2. Kvazar

    Я не уверен, что данный скрипт корректно работает на Sharepouint 2010, разрабатывалось для WSS 3.0 или Sharepoint 2007, о чем я указал в статье.

  3. Клочков Юрий

    Рустам, для удаления используй следующий код, тогда все паботает.

    $spListItems = $splist.get_items() #Полностью очищаем список
    for ($i=($spListItems.Count – 1); $i -ge 0; $i–)
    {
    $spListItems.Delete($i);
    }

    • Рустам

      Спасибо Юрий! Я накорябал сам следующую конструкцию:
      ==================
      #Полностью очищаем список
      $items = $splist.getItems()
      while ($items.count -ne 0)
      {
      for ($i=0; $i -lt $items.count; $i++) { $items.Delete($i) }
      }
      ===============

      Не знаю почему, но простой цикл for без while у меня удалял только часть объектов, например штук 15-20 из 30. С чем это связано, я так и не разобрался. Поэтому у меня цикл работает пока все не очистит )))

  4. Сергей

    Все хорошо конечно же. Но тогда получается, что он каждую ночь заново удаляет и добавляет одних тех же пользователей. Довольно не практично. Поэтому чуть чуть доработал ваш скрипт. Сделал так, чтобы сначала пользователй сравнивался со списком взятым из АД и потом в зависимости от пользователя он удаляется или добавляется в SP.

    Add-PSSnapin Quest.ActiveRoles.ADManagement
    [System.Reflection.Assembly]::LoadWithPartialName(“Microsoft.SharePoint”)
    $siteUrl = “http://server” # Адрес сервера Sharepoint Services
    $webName = “/” #Имя узла
    $spSite = new-object Microsoft.SharePoint.SPSite($siteurl)
    $spWeb = $spSite.OpenWeb($webName)
    $listName = “Телефонная книга” #Имя списка
    $spList = $spWeb.Lists[$listName]
    $spListItems = $splist.get_items() #Подготоваливаем список
    $Domain = ‘server.domen.local’ #Адрес контроллера домена, к которому подключаемся”;
    Connect-QADService -Service $Domain
    # Так как много пользоватлей, то делаю выбор из определенной группы. Если надо из всего Ад то просто удаляем -SearchRoot ‘domen/OU’ , ‘domen/OU’
    $userlist = (Get-QADUser -SizeLimit 0 -SearchRoot ‘domen/OU’ , ‘domen/OU’ -Enabled | Select-Object name , DisplayName ,Department , telephoneNumber , physicalDeliveryOfficeName, title , email )

    # Блок удаление пользователей из списка Sharepoint в соответстви со списком взятым из АД

    for ($spu=0;$spu -lt $splistitems.count; $spu++ )
    {$flag=”false”
    for ($adu=0; $adu -lt $userlist.count; $adu++)
    {if ($splistitems[$spu].name -match $userlist[$adu].displayname)
    {$flag=”true”}
    }
    if ($flag -match “false”)
    {$splistitems.Delete($spu)
    }
    }

    # Блок добавление пользователей в SP из АД

    for ($adu=0; $adu -lt $userlist.count; $adu++ )
    {$flag=”false”
    for ($spu=0;$spu -lt $splistitems.count; $spu++)
    {if ($userlist[$adu].displayname -match $splistitems[$spu].name)
    {$flag=”true”}
    }
    if ($flag -match “false”)
    {$spitem = $spList.Items.Add()
    $spitem[“Title”] = $userlist[$adu].DisplayName
    $spitem[“JobTitle”] = $userlist[$adu].Title
    $spitem[“ol_Department”]= $userlist[$adu].Department
    $spitem[“WorkPhone”] = $userlist[$adu].telephoneNumber
    $spitem[“Email”] = $userlist[$adu].Email
    $spitem[“Office”] = $userlist[$adu].physicalDeliveryOfficeName
    $spitem.Update()
    }
    }

    PS. Забыл дописать. Это все работает на Windows 2008R2, Sharepoint Foundation, Powershell.V2

    • Максим

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

  5. Сергей

    хм… чет он переделал все знаки двойных кавычек……. ну надеюсь сами сможете исправить =»false» на =”false” ну и там он как то выборочно еще позаменял

  6. Уведомление:Телефонный справочник на Sharepoint (WSS) с синхронизацией из AD | Блог Бафа

  7. Олег

    Для себя в итоге вот такой скрипт сделал:

    Add-PSSnapin Quest.ActiveRoles.ADManagement
    [System.Reflection.Assembly]::LoadWithPartialName(“Microsoft.SharePoint”)
    $siteUrl = “http://wss-server/” # Адрес сервера Sharepoint Services
    $spSite = new-object Microsoft.SharePoint.SPSite($siteurl)
    $spWeb = $spSite.OpenWeb()
    $listName = “Сотрудники” #Имя списка
    $spList = $spWeb.Lists[$listName]
    $collListItems = $spList.Items;

    # Запрос к AD на выборку пользователей
    $Domain = ‘server.mydomain.com’
    Connect-QADService -Service $Domain
    $userlist = (Get-QADUser -searchroot ‘mydomain.com/OU’ -IncludedProperties DisplayName, title, company, department,
    Office, PostalCode, l, streetAddress, PhoneNumber, Pager,
    Mobile, facsimileTelephoneNumber, Email, physicalDeliveryOfficeName, Manager, Name, sAMAccountName)

    # Выборочно удаляем пользователей

    $spListItems = $splist.get_items()

    #Write-Host “SP query items count” $splistitems.count

    for ($spu=0;$spu -lt $splistitems.count; $spu++ )

    { $splistitem = $splistitems[$spu]

    $flag=”false”
    for ($adu=0; $adu -lt $userlist.count; $adu++)

    {
    #Write-Host “AD username” $userlist[$adu].sAMAccountName $adu “SP counter” $spu “SP username” $splistitem[“sAMAccountName”]
    if ($splistitem[“sAMAccountName”] -eq $userlist[$adu].sAMAccountName)
    {
    #Write-Host “AD username” $userlist[$adu].sAMAccountName $adu “SP counter” $spu “SP username” $splistitem[“sAMAccountName”]
    $flag=”true”
    #Write-Host $flag
    }
    }
    if ($flag -match “false”)
    {
    #Write-Host “DELETE ” “AD username” $userlist[$adu].sAMAccountName $adu “SP counter” $spu “SP username” $splistitem[“sAMAccountName”] $flag
    $splistitem.Delete()
    }
    }

    # Добавляем новых
    for ($adu=0; $adu -lt $userlist.count; $adu++)
    {$flag=”false”
    $user = $userlist[$adu]
    #Write-Host “AD” $User.sAMAccountName

    for ($spu=0;$spu -lt $splistitems.count; $spu++ )
    {
    $splistitem = $splistitems[$spu]

    #Write-Host “SP” $splistitem[“sAMAccountName”]
    #Write-Host “AD username” $user.sAMAccountName $adu “SP counter” $spu “SP username” $splistitem[“sAMAccountName”]
    if ($user.sAMAccountName -eq $splistitem[“sAMAccountName”])
    {$flag=”true”}
    }
    if ($flag -match “false”)
    {

    $spitem = $spList.Items.Add()
    $spitem[“Title”] = $user.sn +” ” + $user.givenname -replace “”
    $spitem[“JobTitle”] = $user.Title -replace “”
    $spitem[“ol_Department”]= $user.Department -replace “”
    $spitem[“Company”] = $user.Company -replace “”
    $spitem[“WorkPhone”] = $user.PhoneNumber -replace “”
    $spitem[“CellPhone”] = $user.physicalDeliveryOfficeName -replace “”
    $spitem[“WorkFax”] = $user.facsimileTelephoneNumber -replace “”
    $spitem[“Email”] = $user.Email -replace “”
    $spitem[“Office”] = $user.physicalDeliveryOfficeName -replace “”
    $spitem[“Руководитель”] = $user.Manager -replace “”
    $spitem[“Username”] = $user.Name -replace “”
    $spitem[“sAMAccountName”] = $user.sAMAccountName -replace “”
    $spitem.Update()

    #Write-Host “Adding Users AD username” $user.sAMAccountName “SP counter” $spu “SP username” $spitem[“sAMAccountName”]

    }
    }
    #Обновление всех полей
    for ($adu=0; $adu -lt $userlist.count; $adu++)
    {$flag=”false”
    $user = $userlist[$adu]
    #Write-Host “AD” $User.sAMAccountName

    for ($spu=0;$spu -lt $splistitems.count; $spu++ )
    {
    $spitem = $splistitems[$spu]
    if ($user.sAMAccountName -eq $spitem[“sAMAccountName”])
    {$flag=”true”
    #Write-Host “User match found AD – ” $user.sAMAccountName ” and SP – ” $spitem[“sAMAccountName”]
    $spitem[“Title”] = $user.sn +” ” + $user.givenname -replace “”
    $spitem[“JobTitle”] = $user.Title -replace “”
    $spitem[“ol_Department”]= $user.Department -replace “”
    $spitem[“Company”] = $user.Company -replace “”
    $spitem[“WorkPhone”] = $user.PhoneNumber -replace “”
    $spitem[“CellPhone”] = $user.physicalDeliveryOfficeName -replace “”
    $spitem[“WorkFax”] = $user.facsimileTelephoneNumber -replace “”
    $spitem[“Email”] = $user.Email -replace “”
    $spitem[“Office”] = $user.physicalDeliveryOfficeName -replace “”
    $spitem[“Руководитель”] = $user.Manager -replace “”
    $spitem[“Username”] = $user.Name -replace “”
    $spitem[“sAMAccountName”] = $user.sAMAccountName -replace “”
    $spitem.Update()

    #Write-Host “Updating Users AD username” $user.sAMAccountName “SP counter” $spu “SP username” $spitem[“sAMAccountName”]
    }
    }

    }

    Поля немного другие, не совсем по статье, но идея понятна. Может быть, кому-нибудь пригодится.

  8. Михаил

    Интересненько, но можно попробовать сделать немного наоборот. Контакты из списка Sharepoint отправлять в AD в виде Contact. Очевидные плюсы, будет возможность выбирать эти контакты из адресной книги почтового клиента, да и поручить вести данный список можно любому сотруднику.
    Может кто набросает? )))

    ЗЫ: имею ввиду внешних контактов организаци
    ЗЗЫ: для внутренней тел. книги организации ваш скрипт впринципе хорош, минус в том что вести его придётся администратору…

  9. Александр

    при попытке подконектится к порталу
    $spSite = new-object Microsoft.SharePoint.SPSite(“http://portal”)
    пишет ошибку – New-Object : Исключение при вызове “.ctor” с “1” аргументами: “Не удалось найти веб-приложение по адресу http://portal. Проверьте, правильно ли введен URL-адрес. Если этот URL-адрес предназначен для существующего контента, возможно, системному администратору потребуется добавить новое сопоставление URL-адреса запроса с данным приложением.”

    Скрипт запускаю на сервере wss, если через Designer конектится к http://portal – все норм.

    Подскажите чеза?

  10. Ислам

    то что надо, только вот один вопрос, работает ли это (то что в каментах) на 2010 шарике ? или только wss 3.0 и 2007 ? Автору статьи конечно же респектую.

  11. Дмитрий

    А вот интересно, можно похожим образом вытягивать из AD группы и размещать в том же списке с пользователями?

  12. 1baddog

    Добрый день,

    Уточните, ActiveRoles Management Shell for Active Directory от компании Quest Software данное ПО получается нужно устанавливать на сервер? На сайте триальная версия на 30 дней, если устанавливать, то потом будет работать?

  13. 1baddog

    при запуске скрипта ошибка:
    Нельзя вызвать метод для выражения со значением NULL.
    C:\AddUsersAndContactToWSSintoAD.ps1:7 знак:18
    + $splist.get_items <<<< () | % { $_.Delete() } #Полностью очищаем список
    + CategoryInfo : InvalidOperation: (get_items:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

  14. 1baddog

    Не удается индексировать в массив NULL.
    C:\AddUsersAndContactToWSSintoAD.ps1:22 знак:11
    + $spitem[ <<<< "JobTitle"] = $user.Title -replace ""
    + CategoryInfo : InvalidOperation: (JobTitle:String) [], RuntimeException
    + FullyQualifiedErrorId : NullArray

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

Ваш адрес email не будет опубликован.

три + тринадцать =

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