Записи с темой: powershell (список заголовков)
17:40 

Буфер обмена и загрузка файлов #2

Stance Dance

Теперь - банановый (с)
В смысле - теперь это чудо умеет общаться с BITS:


К слову сказать, сама BITS на редкость придирчива. В цикле скормить ей сразу несколько файлов нельзя. Если требуется - список нужно оформить в виде csv-файла, а уже этот файл дать ей на вход. Если же пытаться обойтись без файла - можно будет только создать несколько заданий, в каждое из которых трамбуем один файл, то есть получится что-то вроде этого:


Посмотрим, что будет лучше.
UPD. Спасибо Cybeon. Нафиг временный файл:

@музыка: Wuauquikuna - Sweetheart Merceditas

@темы: Scripting, PowerShell

17:55 

Буфер обмена и загрузка файлов.

Stance Dance

Надоело при помощи цопипасты загружать файлы из нескольких ссылок. Выделить ссылку, скопировать, вставить в браузер. Дождаться, пока вылезет окно сохранения файла, нажать ОК. А потом все это повторить n раз... На-до-е-ло. Хочу так: есть куча ссылок (прямых), выделить их все, скопировать в буфер, нажать 1 (одну) кнопку и получить все разом.
Заодно отметим переход на Powershell 4 (давно пора было это сделать, ISE стал заметно удобнее).

@музыка: Iron Savior - Starborn

@темы: PowerShell, Scripting

15:42 

Powershell + Gmail + Imgur.com = ... part 3

Stance Dance

А зачем нам вообще какая-то выделенная папка для хранения фотографий, которые нужно отправить в веб? У нас же есть потрясающий пункт меню "Послать на... Send to...". Так почему бы им и не воспользоваться?
Для начала настраиваем сам ярылк, который будет лежать в папке %USERPROFILE%\AppData\Roaming\Microsoft\Windows\SendTo
Создаем там новый ярлык, в свойствах которого в поле Target прописываем следующее:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -f "PATH-TO-UPLOAD-sсript"
Имя и значок этого ярлыка выбираются по собственному вкусу.
А скрипт, который будет заниматься непосредственно отправкой, будет выглядеть так:

@темы: PowerShell, Scripting

01:18 

Powershell + Gmail + Imgur.com = ... part 2

Stance Dance

Небольшое дополнение к скрипту отправки картинок на Imgur, закрывающее несколько моментов.
1. Считается, что в папке $UploadFolder что-то есть. Но ведь это не всегда так, от ошибок никто не застрахован. Потому проверяем эту папку на наличие там чего-либо. Если нет ничего - просто завершаем работу:


2. После того, как скрипт работу завершит, уже загруженные файлы стоило бы удалить, чтоб не мешались. Для этого последний оператор $smtp.send() оборачиваем в следующую конструкцию:

Для чего такие сложности? Из-за того, что по завершении отправки среда Powershell не прекратит ссылаться на наши файлы, и эту связь нужно сначала разорвать. Отдельно для каждого вложения и затем для всего письма в целом. После того, как связи разорваны, можно удалить все отправленные файлы. Что и делается в блоке finally. Блок же catch нужен для того, чтобы отследить любую ошибку во время передачи файлов, перехватить ее, опять таки, разорвать связи с файлами и завершить скрипт, не удаляя сами файлы.

3. Предположим, что в папке $UploadFolder пользователь хранит не только те файлы, которые предназначены к отправке (всякое бывает, помним?). Музыка, тексты, еще что-то. Выберем из них только те, что можно отправлять. Это JPG,GIF, PNG.


Окончательный вариант скрипта после всех этих изменений - ниже.

@музыка: Stellardrone - Light Years

@темы: PowerShell

23:18 

Powershell + Gmail + Imgur.com = ...

Stance Dance

Поднадоел photobucket.com. Вот реально поднадоел. Интерфейс неповоротливый, реклама всякая опять же.
Присмотрелся поближе к imgur.com. Раньше он меня жутко бесил своими тормозами, сейчас его в этом плане облагородили. Но не только в этом. Есть там интересная возможность - заливать картинки через почту. Весьма удобно для пакетной обработки, а еще это дело можно заскриптовать.
Не думал, что эта задачка будет настолько интересной :)


А теперь разбираем по складам.
Весь блок #setting up variables ничего особого из себя не представляет. Там описываются все параметры доступа к картинкам и ящику, через который будем все отсылать. Мой ящик - на gmail.com, потому работаем на примере этого почтового сервиса.
Выбрать все картинки из указанной папки - тоже ничего особого. Вся коллекция будет сохранена в переменной $files.
Блок #setting up smtp client уже любопытнее. Там любопытна конструкция

$smtp.Credentials = New-Object System.Net.NetworkCredential($SMTPUsername, $SMTPPassword)
$smtp.EnableSSL = $true

которая говорит скрипту, что с почтой надо общаться по зашифрованному каналу связи. В противном случае gmail пошлет нас подальше с вердиктом

The SMTP server requires a secure connection or the client was not authenticated. The server response was: 5.5.1 Authentication Required

Еще одна деталь. Для того, чтобы это все сработало необходимо уже в настройках аккаунта gmail включить доступ для небезопасных приложений. Материал по этому поводу в сети есть. Если этого не сделать - gmail будет отбивать любые попытки работы скрипта с ящиком.
Блок #settung up message также не представляет ничего особого. Более того, там обязательными для заполнения полями будут всего лишь $msg.To.add($MailTo) и $msg.From = $MailFrom (Куда и от кого, соответственно). Адресом отправки всегда будет upload@imgur.com, а по адресу отправителя будет ясно, какая учетка на самом Imgur.com шлет картинки.
А вот секция #attach files in upload folder выпила у меня крови в три своих горла.
Сама по себе она стандартнейшая. Для каждого файла в коллекции $files, собранной ранее, делаем вложение в письмо с этим файлом. Все бы ничего, но в первой версии этого блока картинки цеплялись к письму и отсылались, но на Imgur ничего не появлялось. В чем проблема?
Как раз пока писалась первая версия скрипта, шел диалог с Линда Кайе по поводу ранее не срабатывавшей отправки картинок. Среди предположений были и лишние символы в имени файлов, и длина имени... В итоге сошлись на том, что картинки рушил формат письма. Линда, я тебя и огорчу, и порадую. Это не совсем так.
Сравнивая два письма, посланных через скрипт и через web-интерфейс самого Gmail, выяснил, что формат их был один и тот же - multipart/mixed. Тем не менее, скриптовое письмо вложения по дороге теряло. А вот в чем отличие нашлось, так это в формате самих вложений: в письмах, отправленных через web-интерфейс, формат картинок JPG показывался как Content-Type: image/jpg. А в сообщениях, сформированных скриптом, те же картинки шли в виде Content-Type: application/octet-stream. И вот именно это и не позволяло уже самому Imgur найти в письме картинки и обработать их. Для того, чтобы избавиться от этой напасти в блок формирования вложений в скрипт и добавлена строка:

$MailAttachment.ContentType.MediaType = "image/$($file.extension.substring(1))"

Причем действует она хитро. В зависимости от того, какое расширение у обрабатываемой картинки, она сменит тип контента. Есть JPG - будет "image/JPG", обрабатываем PNG - получим "image/PNG".
Завершающая же строка скрипта проста как пара копеек, просто отправляем сформированное письмо.

@музыка: Koan - Back to the Silent Lagoon - Blue Mix

@темы: PowerShell, Scripting

00:21 

MP3 Tags in CLI

Stance Dance

Настала пора окончательно разобраться со своей большушей подборкой Two Steps from Hell. Все лень было теги расставить, ведь после конвертации из WAV их там просто нет. А некрасиво.
Нативно PowerShell работать с тегами не может. Вроде как кто-то даже писал стороннюю библиотеку для этих целей, но с ней ничего не сложилось. Что ж, будем использовать старый добрый invoke-expression, который так хорошо выручил в прошлый раз. В качестве инструмента расстановки тегов будет выступать id3.exe - мелкая и быстрая (реально быстрая) утилитка. Снова ISE в зубы и вперед:


Согласен, конструкция ([uri]$file.fullname).segments[5].trim("/").replace("%20", " ").replace("%23", "#").replace("_", " ").substring(7) выносит мозг, но уж что поделать. Ибо в получаемом имени каталога пробелы будут преобразованы в код %20, а знак # - в код %23, что не есть хорошо. Ну и замена подчеркивания на пробел туда же, хотя проще было переименовать исходную папку (что и было сделано уже после прогона скрипта). Отдельно по поводу конструкции .substring(7). Альбомы названы так: XXXX - Album Name, где ХХХХ - год выпуска. Потому его надо отсечь.
Пара минут, и 1049 файлов обработаны так, как требуется по условиям задачи.

P.S. Ну и на сладкое. Всем поклонникам 8-битной музыки - настоятельно рекомендую пройти вот сюда: vk.com/rus_instrumental. Альбом "Ура товарищи, космос наш!" - это нечто. Особенно хочется отметить композиции Железная Пыль и Прыжковый Ранец (must hear интервал 2:54-3:18). А за анимешную мордаху в качестве обложки альбома - просто гранд мерси! :)

@музыка: Злые роботы - Железная Пыль

@темы: Scripting, PowerShell

03:35 

Права на каталоги

Stance Dance

Задали тут задачку на прошлой работе. Строго говоря, задали ее не мне, ибо я там уже не работаю, но проблему озвучили при мне, и в голове тут же щелкнуло - это ж работа обычного скрипта, а чего б его не накатать для тренировки?
Задача состоит в следующем. Есть каталог. На этот каталог привешен доступ для определенной учетки на чтение всего, что в нем лежит. От имени этой учетки работает спец. ПО, которое таскает фотографии из подпапок этого здорового каталога в информационную систему. Беда в том, что на некоторых подпапках (почему-то) выставляются совершенно дикие разрешения, среди которых нужной учетки просто нет. Соответственно, фотографии из этой подпапки не попадают в ИС, после чего вопли до небес и т.д., и т. п., и ДТП.
Почему разрешения могут плыть - отдельный разговор, есть подозрение, что такие каталоги не создаются пользователями, а перемещаются из других папок с сохранением прав. Это еще предстоит проверить, а пока нужно найти все такие подкаталоги и поправить разрешения на них - просто сбросив их в наследуемые от родителя. Сказано - сделано:

Получившая строчка, где определяется переменная $ace - нечто, но оно мне нравится. Если непонятно, объяснение выглядит вот так:
- берется список разрешений на каталог, имя которого получаем в цикле foreach (список будет в памяти в виде объекта),
- раскрываем в этом объекте нужное нам свойство - access. Именно там перечислены все учетные записи, которые есть в списке прав доступа,
- пробегаемся по всем элементам в этом свойстве и ищем там запись, совпадающую с заданным пользователем,
- эту самую полученную запись заносим в переменную $ace (Access Control Entry)
Много слов, а строка одна, и, в общем-то, довольно простая :)

@музыка: Manowar - Black wind, fire and steel

@настроение: Рабочее

@темы: PowerShell, Scripting

05:55 

Powershell variables expansion

Stance Dance

UPD. Ага, а теперь выдолби на том же лбу то, о чем тебе уже говорили: hikedaya.diary.ru/p164830971.htm


Иными словами - у нас появилась новая переменная с именем, состоящим из выражения.
---
Блин, выдолби уже у себя на лбу простую истину:

не даст то, что нужно, будет ахинея. А вот так:

выведет требуемый простое список имя. Да и вообще - раскроет переменную $comp.name так, как надо, не только в случае с компьютерами. Полезно, когда потом то, что загнано в $compname, уйдет в оператор invoke-expression, вызывающий какую-нибудь DOS утилиту. Например, выполняемое для коллекции серверов:

запись создана: 12.01.2015 в 16:17

@темы: PowerShell, Scripting

14:05 

Conversion

Stance Dance

Когда-то давно попалась мне папочка под названием Two Steps from Hell. А в ней куча треков от одноименной группы, все в формате WAV. Чтоб без потери качества, так сказать. Одно плохо, в телефон не закинешь, да и места много едят. Надо бы их всех отконвертировать в идеологически верный mp3, который понимает практически все звукоиздающее барахло. Хотя нет, идеологически верным будет ogg, но мой телефон все равно его не поймет.
Вроде бы на компе есть AIMP с его конвертером. И в большинстве случаев этого хватает. Но в случае пакетной обработки он может и спасовать. Что ж делать, не руками же все это перелопачивать (папок там немало).
Кинул клич по своему контакт-листу, общими усилиями достали завалявшийся lame.exe. Дело за малым, все тот же Powershell в руки:


Крутится, жрет папку, на которую я его натравил...
Забавляет вывод от lame.exe в окне ISE - он весь краснющий, как помидор. Красный в PSH - цвет ошибок, сначала зело напугал :)
Ах, да. Если нужно удалить исходный файл сразу после преобразования - раскомментировать строку #Remove-Item $file.fullname. Без предварительного теста (например, с ключом -whatif) - не рекомендуется, впрочем, так всегда.
P.S. А потом смотрим на строку с определением целевого имени файла и понимаем, что можно было сделать еще красивее:

$destination=$file.fullname -replace $file.extension, ".mp3"

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

@музыка: Roxette - Fingertips

@темы: Scripting, PowerShell

19:27 

DPM 2012 Enumerate tapes scritpt

Stance Dance

Забавный случай. Имеем скрипт, который обсчитывает все ленты в библиотеке, подконтрольной DPM 2012. Фрагмент скрипта выглядит вот так:

$RPs=Get-RecoveryPoint -Tape $Tape
$ExpDate = $RPs[0].recoverysourcelocations[0].expirydate

И вот этот кусок ведет себя на разных системах по-разному. На одной из рабочих станций - все ок. На сервере - все ок. На другой рабочей станции - все плохо, дает ошибку вида Unable to index into an object of type...
Сначала думали, что что-то не так с самим шеллом от DPM на проблемной машине. Переставили, результат нулевой.
Проверили догадку относительно прав на сам DPM и работу с ним. Отмели, потому что на сервере под той же учеткой, что и на проблемной рабочей станции, все ок.
Начал ковырять. Скрипт валится всегда на одних и тех же кассетах. А записей на этих кассетах - по одной штуке на каждую. Получается, что на выходе мы получаем обычную скалярную переменную, которую скрипт затем пытается представить как массив, и выдрать из него самый первый элемент с нулевым индексом. И получает отлуп. Проверяем:

PS C:\temp> $a=12
PS C:\temp> $a[0]
Не удается индексировать в объект типа System.Int32.
строка:1 знак:4
+ $a[ <<<< 0]
+ CategoryInfo : InvalidOperation: (0:Int32) [], RuntimeException
+ FullyQualifiedErrorId : CannotIndex

Уже интереснее. Ковыряние сети привело к корню проблем: на такой фокус с обращением к обычной переменной, как к массиву, способен Powershell 3.0 и выше. А на проблемной станции - увы и ах, всего лишь вторая.
Что ж, учтем на будущее.

@музыка: Koan - Dance of Nereids

@темы: PowerShell, Scripting

03:25 

Event Log + Powershell

Stance Dance

MS в своем стиле. Давно такой зубодробиловки не встречал. Впрочем, из стана *nix, наверняка, наоборот, могут послышаться возгласы одобрения, ибо "все есть файл".
Задача. Необходимо сделать так, чтобы по возникновению в журнале Windows события с определенным кодом администратору (или другому причастному к процессу) на почту падало письмо с содержанием этого сообщения. Заскриптовать сам процесс отправки письма - не проблема. Проблема в это письмо утрамбовать содержимое самого сообщения.
Что попытались сделать? Нам известен код события. Что ж, по возникновению этого события ищем все события с этим кодом, сортируем их в порядке убывания, отсекаем самое первое в выборке. Это и будет искомое. Дальше по скрипту вытаскиваем его содержимое, трамбуем в письмо, отсылаем депешу. Казалось бы, проблема решена.
А вот и нет. Проблемы начинаются, когда копируется сразу несколько мелких файлов. В этом случае вся наша выборка становится сбойной, и в итоге события теряются, письма приходят только на часть их. Что же делать?
Вечерний треп с Cybeon принес весьма любопытные результаты. У каждого события в логе есть уникальный номер. Вот вытащить бы его, отдать скрипту, а скрипт уже пусть ищет именно это конкретное событие и пакует его в письмо. Идея хорошая, но как? А вот так:
blogs.technet.com/b/otto/archive/2011/08/24/tri...
Как уже выше было сказано - все есть файл. Именно файл нам на помощь и приходит.
Вкратце - создаем болванку задания, экспортируем его в XML файл и убиваем болванку. Она нам больше не нужна. А потом правим полученный XML файл и вносим туда следующее:

После чего импортируем этот файл в оснастку назначенных заданий. После таких вот премудростей наше задание будет отдавать три параметра: имя журнала, в котором возникло наше событие (eventChannel), его критичность (eventSeverity) и, та-дааааам, уникальный код этого события (eventRecordID).
Итак, сами значения мы выцарапали. Как их передать в скрипт? Через параметры командной строки:
powershell -command %PATH-TO-sсript% -eventChannel $(eventChannel) -eventRecordID $(eventRecordID) -eventSeverity $(eventSeverity)

А последний шаг - уже в том скрипте, который будет выполнять все действия по отсылке писем, первой строкой объявляем полученные параметры:
param($eventChannel,$eventRecordID,$eventSeverity)

На этом все. Но осталась важная ремарка - не дай вышние ошибиться хоть где-нибудь в регистре в именах всех этих переменных. Как оказалось - регистрозависимо тут все.

Почему передачу данных в скрипты из журналов до сих пор не сделали более менее дружелюбной к администраторам - об этом остается только гадать. Несмотря на все улучшения в Windows 2012 - пилить ее еще и пилить.

@музыка: Eric "Erock" Calderone - Killer Instinct Meets Metal

@темы: PowerShell, Scripting

10:23 

Remote Desktop Connection Manager + Active Directory #2

Stance Dance

Скрипт доработан до полного функционала. Берет информацию о серверах из Active Directory, вносит ее в файл настроек RDCMan, затем убирает из этого файла настроек записи о серверах, которых уже нет в AD. Доработка напильником как всегда: указать путь к файлу настроек и задать критерии отбора серверов:

@музыка: Power of Melody - Poisoned Planet

@темы: Scripting, PowerShell

15:18 

Remote Desktop Connection Manager + Active Directory

Stance Dance

RDCMan попался мне на глаза довольно давно. Удобный инструмент для управления соединениями через RDP, плюс штампик "от производителя". Этот самый штампик и позволяет его использовать на рабочем месте. Да вот беда - эта программка ведет свой собственный список серверов, с Actove Directory никак не связанный. А это плохо, лениво каждый раз при вводе нового сервера добавлять его в лист RDCMan. Что же делать? Каким-то образом автоматизировать обновление списка, больше нечего.
Список серверов RDCMan хранит в специальном файле с расширением *.rdg (remote desktop group), который по своей сути является обычным XML-файлом. Вариантов его ведения может быть два - либо все серверы, добавленные в эту группу будут идти единым списком (именно это требуется и мне), либо же будут вручную разбиты по группам внутри списка. С ручной группировкой ничего не сделаешь - в AD нет сведений, по которым можно было бы серверы по группам раскидывать, а вот с простым списком можно и поковыряться. Powershell to the rescue!
Для начала заставим RDCMan создать файл группы серверов. Просто запускаем его и добавляем туда сервер-болванку, назначение которой - быть шаблоном для добавления остальных серверов. Попутно можно прописать туда общие настройки подключения - в 90% случаев все подключения проходят под одной и той же учеткой, с одинаковыми параметрами. После подготовки начинаем ваять сценарий синхронизации.
Первым делом в сценарии, который будет обновлять список серверов в RDCMan, поднимаем работу с Active Directory:

import-module activedirectory

Само собой, этот модуль уже должен быть установлен в системе.
Далее необходимо загрузить содержимое нашего файла в Powershell (нужно будет прописать путь к rdg-файлу

$doc="path-to-rdg-file"
$xml = New-Object XML
$xml.load($doc)

Следующий шаг - получение списка серверов из AD (необходимо подставить критерии отбора серверов):

$comps=get-adcomputer -filter 'insert-your-criteria'

После получения списка серверов скрипт должен пробежать по списку уже имеющихся серверов в файле и полученному списку из AD и сравнить их. Если будет найден сервер, который присутствует в AD, но отсутствующий в списке RDCMan - добавим его.

foreach ($comp in $comps) {
$isExist=$False
foreach ($node in $xml.rdcman.file.server) {
if ($node.name -eq $comp.name) {$isExist=$True}
}

if ($isExist -eq $False) {
$clone = $xml.rdcman.file.server[0].clone()
$clone.name=$comp.name
$clone.displayName=$comp.name
$clone
$xml.rdcman.file.appendchild($clone)
}
$isExist=$False
}

В завершение - сохранение нашего файла RDCMan одной простой командой:

$xml.save($doc)

К чести RDCMаn стоит добавить, что сортировку серверов по алфавиту он выполнит автоматически при запуске (отключаемо). За это некоторые его ругают, но мне этот подход нравится.
Последним шагом будет удаление из списка RDCMan сервера-болванки.
Ну и пара слов по тому, каким образом можно использовать полученный сценарий. Лично мне по душе автоматический его запуск при логине в систему. Таким образом по завершении загрузки профиля список RDCMan'а уже будет актуализирован.
Что еще можно было бы добавить: обратную проверку. Если сервер перечислен в RDCMan, но его записи нет в AD - удалить его из RDCMan.

@темы: Scripting, PowerShell

20:37 

У семи нянек...

Stance Dance

... или как мы Server Core 2012 крутили-вертели.
Именно так, причем ось этого кручения-вращения называть не будем.
Все прекрасно, практически все аспекты настройки выполняются довольно просто и быстро. Имя сервера, домен/рабочая группа, роли, дополнения - в Powershell настраиваются просто на ура. Феерия. Ровно до того момента, когда требуется подкрутить настройки сети. Потому что далее начинается ад.
Вспомним, практически все настройки сети у нас были в одном окне. Да, одном. Открыл нужный интерфейс, поменял, что надо, закрыл. Теперь давайте подсчитаем, что у нас имеется для настройки сети в командной строке:
- старый добрый ipconfig в случае настройки через DHCP (да, да, те самые /release и /renew);
- чуть менее старый и менее добрый netsh, там синтаксис команд вырвиглазный;
- новый и довольно злобный powershell, многообразие команд которого заставляет окунуться в technet, а вынырнуть может и не удастся вовсе. Судите сами:
get-netadapter (если надо свериться с сочетанием интерфейс-сетевой адаптер), set-netipaddress или new-netipaddress (в зависимости от ситуации - задать адрес так или сяк), set-dnsclientserveraddress (тут мы прописываем настройки DNS). А уж если облажались с настройкой шлюза по-умолчанию, то нам сюда, где мы познакомимся еще и с командами Remove-NetRoute и New-NetRoute для удаления неверного и прописи нужного шлюза.
А в итоге - в подавляющем большинстве намного быстрее и понятнее будет сделать следующее:

Где в разделе номер 8 можно настроить сеть как нам угодно.
Что ж, MSы сдержали обещание. В Windows Server 2012 они полностью пришли к Unix-way - все, абсолютно все можно сделать из командной строки. Но некоторые операции, подчас, в тех же никсах, сделаны куда изящнее и проще. Хотя тоже через командную строку.

@музыка: Nicolas Jeandot - Fiery Horse

@темы: PowerShell, Этот безумный мир

21:36 

User account properties

Stance Dance

Следующая подзадача в нашей эпопее - в образе есть дефолтная пользовательская учетка. После того, как образ развернется, этой учетке нужно поставить свойство User must change password at next logon. Естественно, по возможности опять же в powershell сделать.
Методов несколько. Чтобы не томить, привожу тот, что сработал в моем случае:

$username=%INSERT_ACCOUNT_NAME%
$user = [ADSI]"WinNT://$env:computername/$username,User"
$user.passwordexpired=1
$user.setinfo()

Код прост как две копейки. Получить от провайдера ADSI объект нашей учетки, поменять его свойство под названием "Пароль просрочен", подтвердить изменения. Все. Задача выполнена.
А вот в процессе написания выплыл интересный факт. Галка User must change password at next logon в интерфейсе свойств учетной записи связана с вот таким свойством: User Flags. Значение этого параметра вычисляемое и зависит от нескольких факторов. МСы в документации приводят полный список значений, которое это свойство может принять - Клац!
Ок, небольшой эксперимент. В свойствах учетной записи в графическом интерфейсе меняем все галки, кроме самой первой. Это как раз User must change password at next logon. А потом даем простые три строчки:

$username=%INSERT_ACCOUNT_NAME%
$user = [ADSI]"WinNT://$env:computername/$usenamer,User"
$user | fl *

И наблюдаем, что параметр UserFlags принял значение 8389121. Закономерный вопрос - как так?! Ответить на него еще только предстоит. А пока - добиваем в скрипт окончательной настройки системы после развертывания полученный ранее блок команд, и наслаждаемся результатом.

@музыка: Laliya - Just like river

@темы: PowerShell, Scripting

19:25 

Download KVRT in Powershell

Stance Dance

Когда-то давно довелось мне написать скрипт автоматической загрузки Kaspersky Virus Removal Tool. Как показало время - запись та была достаточно популярной. Как и сам KVRT. Поскольку же прогресс не стоит на месте, и многие все же переходят на ОС Windows 7 и выше, специально для них (и для себя любимого в их числе) переработал тот сценарий, переписав его в Powershell. Вон он:

Download KVRT.ps1

Обработка напильником стандартная: в строке $folder = "d:\KVRT" прописать имя того каталога, куда будет складываться загружаемый файл.
ВНИМАНИЕ! Содержимое каталога, указанного в переменной $folder, будет удалено в процессе выполнения этого сценария. Это сделано специально, исходя из собственных нужд. Если очистка каталога нежелательна, нужно закомментировать строку remove-item $folder\*.* при помощи символа # (или вообще удалить ее).
Ну а о том, как можно Powershell-скрипт запускать по расписанию, в сети материала вагоны )

@музыка: Adrian von Ziegler - The Stormbringer

@темы: Viruses and Spam, Scripting, PowerShell, Kaspersky Lab

12:56 

Parse and sort

Stance Dance

После развертывания MDT, которым сейчас вовсю пользуются ребята из службы поддержки, возникла одна проблема. Большая она или нет - это с какой стороны взглянуть. Суть в следующем - когда рабочая станция получает образ ОС и вводится в домен при помощи MDT, она попадает в базовую OU Computers. В то же время у нас AD разбита по отделениями и филиалам, на каждый филиал своя OU. И теперь нужно вручную все эти компьютеры раскидать по различным OU. Лениво. Впрочем, так скажет любой администратор. Что же делать?
У каждого отделения свой диапазон IP адресов. Все IP адреса есть в DNS. Стало быть, можно определить, какой компьютер из базовой OU куда должен улететь. Остается лишь получить IP адрес компьютера и сказать скрипту, чтобы он принял решение о переносе компьютера в нужную OU именно на основании полученного адреса. Но как это сделать? Стандартная команда nslookup выдаст нам море лишней информации - имя контроллера домена, его адрес, имя интересующего нас хоста... Более того, все это будет в нескольких строках. Значит, придется парсить эти строки, ничего не поделать. Снова и снова берем в руки Powershell и начинаем размышлять.
Утрамбовать вывод стандартного nslookup в какую-либо переменную труда не составляет:
$var = nslookup hostname
Дальше пропишем небольшую модель нашей сети. Итак, пусть диапазоны адресов, которые будут использоваться, выглядят так:
Главный офис - 10.10.1.1 - 10.10.6.255
Филиал 1 - 10.20.1.0/24
Филиал 2 - 10.20.2.0/24
Филиал 3 - 10.20.3.0/24
...

В главном офисе стоят все серверы, их подсеть - 10.10.1.1-10.10.1.254. Среди них и сидит наш DNS сервер, который будет отдавать информацию по nslookup. Диапазон адресов рабочих станций в центральном офисе - 10.10.3.1 - 10.10.6.255.
Стало быть, когда будем парсить вывод nslookup, нам нужно будет игнорировать в выводе адрес вида 10.10.1.x - адрес нашего DNS - он бесполезен.
Стандартный вывод nslookup будет выглядеть следующим образом:
c:\> nslookup hostname

server: DNS-fqdn
Address: DNS-ip

name: hostname
Address: hostname-ip
Последняя строка может приять вид Addresses: hostname-ip1, hostname-ip2 если адресов больше одного.
Итак, нас будет интересовать последняя строка. Для построчного анализа можно использовать следующую конструкицю
foreach ($line in $var) { sсript-block }
В переменную $line будут поочередно заноситься все строки, хранящиеся в многострочной переменной $var, и уже переменная $line будет подвергаться анализу, что нам и нужно. Основной вопрос - как проверять? Тут на помощь придет трава (иначе и не скажешь) под названием регулярные выражения. Выглядеть в реализации Powershell это будет следующим образом:

Под CO тут понимается центральный офис (он же главный офис).
На этом самая заковыристая часть скрипта написана. Остается только сделать выборку компьютеров из базовой OU Computers, прописать механизм переноса записи компьютера между OU, да перечислить все возможные блоки в цикле foreach ($line in $var) { }. Все это уже тривиально.
Ну и попутно придется курить руководства по MDT в плане возможности выбора OU, куда компьютер будет сразу прописываться.

@музыка: RA - Creation of Tefnet

@темы: Scripting, PowerShell

16:10 

Windows Distribution Point #2

Stance Dance

В предыдущей записи было рассказано, как можно подготовить пачку обновлений для интеграции их в дистрибутив ОС. Следующий шаг - непосредственно внедрение. Делается просто - вызываем интересующий нас файл с параметром /integrate, в котором прописываем путь к дистрибутиву. Само собой, что дистрибутив нужно скопировать с компакт-диска, ведь на сам компакт просто так ничего не запишешь. Ну и само собой - язык ОС должен совпадать с языком обновлений, иначе интеграция не пройдет.
Хорошо, ну один файл, ну два, ну три... А когда их нужно под сотню обработать? Не руками же все это каждый раз прописывать. Верно, не руками. Снова запускаем ISE и пишем там следующее:

Где E:\WinXP-Update-Pack - имя каталога с файлами обновлений, d:\winxp - каталог с файлами дистрибутива Windows XP SP3.
Скрипт сначала прочитает список файлов обновлений, а потом будет обрабатывать их по одному, попутно выводя на экран сообщение о том, что именно в данный момент встраивается в дистрибутив. Процесс довольно долгий, можно выпить не одну чашку чая/кофе. В моем случае пришлось на это положить час, завалившись на диван с прелестной книгой.
По окончании процесса нужно будет лишь из каталога d:\winxp (с моем случае) собрать ISO файл, который уже можно использовать для установки ОС.
На первый взгляд - все просто. Но - как бы не так. При попытке установки Windows XP из полученного дистрибутива получаем странное:
lsass.exe - недостаточно квот на виртуальную память или файл подкачки для завершения требуемой операции.
Как подсказывает всемирный разум - такое поведение наблюдается при интеграции в дистрибутив, помимо всего прочего, еще и Internet Explorer/Windows Media Player старших версий. Убрал файлы этих обновлений из каталога перед интеграцией - проблема решилась. Ладно, эти продукты можно и через WSUS ставить.

@музыка: Poets of the Fall - May be tomorrow is a better day

@темы: PowerShell, Scripting

15:55 

Windows Distribution Point.

Stance Dance

Идея единой точки распространения дистрибутива Windows на работе занимает мой мозг уже давно. В принципе, этот сервис уже поднят, настроен и работает. Связка WDS + MDT 2010 отработала просто прекрасно, стоило потратить пару дней на "вкуривание" процесса настройки MDT. Но остался нерешенным один вопрос - что делать с обновлениями ОС после установки? Да, поднят и работает WSUS, но сам процесс установки, когда машина тащит и натягивает на себя больше сотни обновок, просто очень долог. Остается только один вариант - интеграция обновлений непосредственно в дистрибутив. После установки ОС уже будет пропатчена по самое не могу. Идея хорошая, но где взять эти обновления? Не руками же с сайта Windows Update вытаскивать, хотя и была такая идея.
Сначала попробовал вынуть из из WSUS при помощи новоразвернутой машины. Да, вынуть их удалось, но уже в распакованном виде. Не пойдет, потому что заветный ключ /integrate применяется только к упакованным обновлениям, а как их паковать таким образом, MS, конечно же, не объясняет, ибо зачем?
Гугл, запрос, курение результатов. Результатом стала следующая ссылка:
support.microsoft.com/kb/913086 - Security updates are available on ISO-9660 DVD5 image files from the Microsoft Download Center
Да, вы не ошиблись. Все апдейты доступны в виде ISO образов. Для всех систем. Трафика, конечно, ушло безумно много - 90 Гб, зато теперь есть все необходимое.
Только вот одна проблема. 56 образов, если считать от месяца выпуска SP3 для Windows XP (меня интересует этот дистрибутив). Каждый из них нужно распотрошить, выбрать оттуда каталог с нужной версией, архитектурой, языком, вытащить оттуда файлы и сложить в отдельный каталог. Ужас. Что же делать? Окидываем взглядом систему, понимаем, что в ней есть следующие компоненты: Daemon Tools, Powershell, ISE. Большего и желать нельзя. Ну и справка от Daemon Tools, конечно же.
Итак, идея в том, чтобы заставить Powershell-скрипт извлекать из образов нужные файлы. Структура каталогов внутри каждого образа известна - номер Knowledge Base, версия ОС, архитектура, язык, например, DriveLetter:\2723135\WindowsXP\x86\*.exe
Заставить извлекать файлы по таким путям - дело нехитрое. А вот что делать с процессом монтирования образов? Тут-то на помощь и придет Daemon Tools. Эта широко известная программка способна работать не только в обычном графическом режиме, но и через командную строку. Ее ключи подробнейшим образом расписаны во встроенной справке, хотя и там не обошлось без накладки, ее я выделю позже.
Теперь перечислим, что у нас имеется (применительно к моей системе):
Файлы всех ISO образов лежат в папке D:\Temp\Архивы
Складывать все обновления мы будем на диск E: в соотвествующие папки.
Daemon Tools установлен в стандартный каталог C:\Program Files (x86)\DAEMON Tools Lite.
Виртуальный диск, в который монтируются файлы-образы, имеет букву G:
Поехали:

В принципе, код не сложен. Отдельно хотелось бы остановиться на паре моментов.
Первый - строка $args = "-mount 0,$iso. Именно здесь задаются ключи запуска Daemon Tools. И именно здесть есть небольшая накладка в справке самой DT. В ней указано, что среди параметров необходимо указывать тип драйва, в который мы стараемся монтировать файл-образ. На практике попытка указать тип приводит к ошибке, поэтому тип драйва просто опущен. А должно было быть вот так: $args = "-mount scsi,0,$iso".
Ну а второй момент - искуственная задержка start-sleep 5 - самой Daemon Tools требуется некоторое время на монтирование образа.
Ради интереса в код ввел вывод времени начала и окончания выполнения. Выяснил, что весь скрипт отрабатывает 5 минут или около того. Представляю, сколько времени я бы все эти действия выполнял руками...
Может возникнуть вопрос - а что за блоки, связанные с обновлениями Win7 и язык такой Neu. Ответ на первую часть вопроса прост - в рамках этого же скрипта еще и для семерки файлы обновлений вытащим. А вот что за локаль такая - я и сам не знаю. Все обновления Win7 идут только в ней. В папках Rus для семерки не оказалось ни одного файла, только в Neu.
... И почему нативная поддержка ISO в Windows появилась только в Win 8...

@музыка: Poets of the Fall - May be tomorrow is a better day

@темы: PowerShell, Scripting

15:49 

Users in groups...

Stance Dance

Недавно обнаружил странную особенность в поведении скрипта обработки учеток уволенных сотрудников. Предполагается, что учетная запись будет вышвырнута из всех групп, кроме базовой Domain Users, после чего заблокирована. Странность в том, что на одних учетках эта процедура отрабатывает на ура (в том числе и на моей тестовой), а на других - на первый взгляд не работает вообще. Вот как это реализовано:
(get-qaduser $user).memberof | Get-QADGroup | where {$_.name -ne "domain users"} | Remove-QADGroupMember -member $user
Начинаем разбор полетов. Берем учетную запись, уже отключенную ранее, видим, что в свойствах AD групп там перечислено довольно много. Понятно, скрипт не отработал. Загоняем данные этой учетки в переменную $user, запускаем строку на исполнение, и видим прекрасное:
Remove-QADGroupMember : Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
Уже интереснее. В голове тут же формируется мысль о том, что сотрудник этот сидит не только в группах нашего домена, но и соседнего, а в свойствах учетки через ADUC такие группы не видны. Что ж, даем следующее:
$col = (get-qaduser $user).memberof | Get-QADGroup | where {$_.name -ne "domain users"}
$col

Так и есть. Кучка групп из других доменов. Скрипт натыкался на такую группу, получал отлуп и прекращал обработку всех групп у этой учетки, переходя к остальным процедурам. Надо исправить:
$colGroups = (get-qaduser $user).memberof | Get-QADGroup | where {$_.name -ne "domain users"}
foreach ($group in $colGroups) { Remove-qadgroupmember -identity $group -member (get-qaduser $user) }

К чему приведет? Скрипт по прежнему будет нарываться на группы соседнего домена, но теперь будет пропускать лишь итерации цикла удаления групп, а не всю коллекцию разом. Что и требовалось.

@музыка: Medwyn Goodall - Snows of Kilimanjaro

@темы: PowerShell, Scripting

Записная книжка

главная