2010-05-20

Powershell и управление принтерами

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

  1. # Ближайший до пользователя принт-сервер. Просто некоторые принтеры подключены к нескольких сервакам. Зачем? Никто не знает.  
  2. $prefer_server = "chel2.example.com"  
  3. # Подрубаемся к домену  
  4. $domain = "LDAP://dc=example,dc=com"  
  5. $root = New-Object DirectoryServices.DirectoryEntry $domain  
  6. $adFind = New-Object Directoryservices.DirectorySearcher  
  7. $adFind.SearchRoot = $root  
  8. # Возьмем имя пользователя из окружения  
  9. $username = $env:username  
  10.   
  11. # Ничего не делать, если скрипт выполняется на win2k3 и у пользователя в DN есть Full Users (То есть фулл юзверь подключился к терминал-серверу)  
  12. # Т.к. у нас в домене есть терминальщики и полноценники, то полноценникам не надо подключать принтера в терминальную сессию  
  13. # Т.к. они у нас в разных OU, проверку выполняем по DN.  
  14. $adFind.Filter = "(&(objectClass=user)(!(ObjectClass=computer))(sAMAccountName=$username))"  
  15. $user = $adFind.FindOne()  
  16. if ($user.Path.Contains("Full_Users")) {  
  17.     $osname = (gwmi -class Win32_OperatingSystem).Caption     
  18.     if ($osname -match ".*2003.*") {              
  19.             exit(0);  
  20.     }  
  21. }  
  22.   
  23. # Перечисление всех доступных принтеров  
  24. $ad_printers = @{}  
  25. $adFind.Filter = "(objectClass=printqueue)"  
  26. $adFind.FindAll() | Sort-Object -Property Properties.servername | foreach-object {  
  27.     if ($ad_printers.Contains($_.Properties.printername[0])) {  
  28.         # Добавить шару принтера на предпочитаемом сервере. Удалить на непредпочитаемом  
  29.         if ($_.Properties.servername[0] -eq $prefer_server) {  
  30.             $ad_printers.Remove($_.Properties.printername[0])  
  31.             $ad_printers.Add($_.Properties.printsharename[0],$_.Properties.uncname[0])  
  32.         }  
  33.     } else {  
  34.         $ad_printers.Add($_.Properties.printsharename[0],$_.Properties.uncname[0])  
  35.     }  
  36. }  
  37.   
  38. # Удалить не локальные принтеры  
  39. # На терминалах удалять драйвера стрёмно. На обычных компах в общем тоже, но сказали надо. Значит удаляём драйверы в том числе.  
  40. # Так же есть еще проблема - принтеры не на принт-серверах, а на локальных компах в филиалах  
  41. # В этом случае при запуске скрипта он удалит нах все сетевые принтаки и будет пытаться подключить перечисленные в AD принтеры  
  42. # А если принтак на локальном компе, и этот комп выключен - принтак не поставится. Пользователь звереет на глазах.  
  43. # Для таких принтеров в комментарии прописываем 'do_not_delete' и скрипт удалять такой принтер не будет  
  44. $del_printers = gwmi win32_printer -Filter "Local='$false'"  
  45. if ($del_printers) {      
  46.     # system name  
  47.     $osname = (gwmi -class Win32_OperatingSystem).Caption  
  48.     $del_drivers = $true  
  49.     if ($osname -match ".*2003.*") {  
  50.         $del_drivers = $false  
  51.     }  
  52.     foreach ($printer in $del_printers) {  
  53.         if (!(  
  54.               ($printer.Comment -ne $null) -and ($printer.Comment.Contains('do_not_delete'))  
  55.               )) {  
  56.             $driver = $printer.DriverName  
  57.             $printer.psbase.Delete()  
  58.             if ($del_drivers) {  
  59.                 write "Deleteting driver $driver"  
  60.                 $rundll32="$env:windir\System32\RUNDLL32.EXE printui.dll,PrintUIEntry /dd /q /m '$driver'"  
  61.                 write $rundll32  
  62.                 invoke-expression -Command $rundll32  
  63.             }  
  64.         }  
  65.     }  
  66. }  
  67.   
  68. # Поиск и установка назначенных принтеров  
  69. $printers_to_install = @()  
  70. $adFind.Filter = "(&(objectClass=user)(!(ObjectClass=computer))(sAMAccountName=$username))"  
  71. # Format: "printer_name:next_printer_name:last_printer_name"  
  72. # Принтеры прописаны в AD у каждого пользователя в поле "Комната". Получим их.  
  73. $printers = ($adFind.FindOne()).Properties.physicaldeliveryofficename -split ":"  
  74.   
  75. if (!$printers) {  
  76.     # no defined printers!  
  77.     write "No defined printers!"  
  78.     exit  
  79. }  
  80. # Найдем в списке всех принтеров AD те, которые нужны нам.  
  81. foreach ($printer in $printers) {  
  82.     if ($printer.Length -ne 0) {  
  83.         if ($ad_printers.Contains($printer)) {  
  84.             $printers_to_install = $printers_to_install + $ad_printers.Item($printer)  
  85.         }  
  86.     }  
  87. }  
  88.   
  89. # Проверить, есть ли локальные принтеры. Если есть - не надо ставить дефолтный принтер  
  90. $local_printers = gwmi win32_printer -Filter "Local='$true'"  
  91. $set_default = $true  
  92. if ($local_printers) {  
  93.     foreach ($local_printer in $local_printers) {  
  94.         # Проверка на "псевдо"-принтеры  
  95.         if ($local_printer.Name -match ".*XPS.*") {  
  96.             $set_default = $true  
  97.         } elseif ($local_printer.Name -match ".*Microsoft.*") {  
  98.             $set_default = $true  
  99.         } else {  
  100.             $set_default = $false  
  101.             $local_printer.setdefaultprinter()  
  102.             break  
  103.         }  
  104.     }         
  105. }  
  106.   
  107. # Установка принтеров  
  108. foreach ($printer in $printers_to_install) {  
  109.     write "installing $printer"  
  110.     $(New-Object -ComObject WScript.Network).AddWindowsPrinterConnection("$printer")  
  111. }  
  112.   
  113. # Установка принтера по-умолчанию. По-умолчанию пропишется тот принтер, что в поле у юзвера стоит первым.  
  114. if ($set_default) {  
  115.     $all_pr = gwmi win32_printer -Filter "Local='$false'"  
  116.     foreach ($printer in $all_pr) {  
  117.         $a = $printer.ShareName  
  118.         if ($printers[0] -eq $a) {  
  119.             $printer.setdefaultprinter()  
  120.         }  
  121.     }  
  122. }  

Скрипт наверняка содержит пару миллионов багов и непредвиденных ситуаций. Писалось на коленке и как обычно - если заработало, значит нормально.

Запускается у клиентов таким батником:

xcopy \\example.com\NETLOGON\printers\printers_chel.ps1 %temp% /Y
%windir%\system32\windowspowershell\v1.0\powershell.exe -NonInteractive -NoLogo -WindowStyle Hidden -Command ". %temp%\printers_chel.ps1"

Так же в group policy добавлен шаблон, который ставит уровень запуска powershell как unrestricted. Иначе powershell будет ругаться, что по политике нельзя запустить левый скрипт, неподписанный.

Вроде работает =)

1 комментарий:

Анонимный комментирует...

Спасибо, полезный материал. Добавил ваш блог в закладки.