Feeds:
Записи
Комментарии

Archive for the ‘PowerShell V3’ Category

Рассмотрим 2 метода:

  1. Используя Com объект  InternetExplorer.Application
  2. Используя командлет Invoke-WebRequest (появился с версии 3.0)

 

Учетом успешного выполнения скрипта, будет вывод сообщения – “Ваш последний визит был: время”

1.  InternetExplorer.Application

Определим, какие поля нам требуется заполнить, достаточно просто.

В поля  “Имя Пользователя” и “Пароль” подставим значения  ,и посмотрим, какие  поля требуют заполнения.

ie

PS >  $ie.Document.forms.Item(1) | Format-Table Name,Value -Auto

name                     value
----                     -----
vb_login_username        abc
cookieuser               1
vb_login_password        abc
                         Войти
s
do                       login
vb_login_md5password
vb_login_md5password_utf

 

Как видно выше для заполнения нужны поля vb_login_username и vb_login_password.


# Имя пользователя
$login = "MyUser"
# Пароль
$pass = "Password"
# Адрес страницы
$url = "http://forum.oszone.net"
# Создание Com-объекта
$ie = New-Object -ComObject InternetExplorer.Application
# Переход к заданному адресу
$ie.Navigate($url)
# Ждем загрузки страницы
while($ie.Busy) { Start-Sleep -Milliseconds 100 }

# Объект страницы
$doc = $ie.Document
# Заполняем поле - Имя пользователя
$doc.getElementsByName("vb_login_username") | % {$_.Value = $login}
# Заполняем поле - Пароль
$doc.getElementsByName("vb_login_password") | % {$_.Value  = $pass}
# Снимаем галочку - Сохранить
$doc.getElementsByName("cookieuser") | % {$_.Value  = 0}

# Находим кнопку войти - нажимаем ее
$doc.getElementsByTagName("input") | ? {$_.Value -eq "Войти"} | % {$_.Click()}
while($ie.Busy) { Start-Sleep -Milliseconds 100 }
# После авторизации, получаем данные
$doc.Body.OuterText.Split("`r`n") -match "Ваш последний визит"

 

2. Invoke-WebRequest

Определим, какие поля нам требуется заполнить у формы.

PS >  $r.Forms[1].Fields | Format-Table -Auto

Key                      Value
---                      -----
navbar_username          Имя пользователя
cb_cookieuser_navbar     1
vb_login_password
s                        7f2a40e2749a7bf18b1b371
do                       login
vb_login_md5password
vb_login_md5password_utf

 

Требуется нам заполнить все поля или достаточно определенных. В этом нам поможет Fiddler.

  • Запускаем Fiddler
  • Запускаем любой браузер ( логирование командлета Invoke-WebRequest тоже поддерживается)
  • Переходим на сайт
  • Заполняем поля , нажимаем “Войти”
  • Смотрим Post запрос

iw

 

Поле vb_login_password – пустое, но вычисляется md5 hash пароля.

$md5 = [Security.Cryptography.MD5]::Create()
$bstr = [Text.Encoding]::ASCII.GetBytes("abc")
$hash = (-join ($md5.ComputeHash($bstr) | %{ "{0:X2}" -f $_})).ToLower()
$hash
900150983cd24fb0d6963f7d28e17f72

Для ASCII символов, если пароль в Unicode, данный метод не подойдет. Какой алгоритм преобразования

пароля содержащего Unicode символы — http://forum.oszone.net/clientscript/vbulletin_md5.js?v=364

Вычислим поле vb_login_md5password для Unicode , на примере – Привет:

$str = -join([char[]]"Привет" | % {"&#"+ ([int][char]$_) +";"})
(-join ($md5.ComputeHash([Text.Encoding]::ASCII.GetBytes($str))| %{ "{0:X2}" -f $_})).ToLower()

«&#»+ [int][char]П –> 1055 + “;” = “П”

Вычислим поле vb_login_md5password_utf для Unicode , на примере – Привет:

$str = [int[]][char[]]"Привет" | % {$_ -band 255} 
(-join ($md5.ComputeHash($str) | %{ "{0:X2}" -f $_})).ToLower()

При использовании полей vb_login_md5password и vb_login_md5password_utf, поле vb_login_password – не заполняем.

Но можно не утруждать себя данными алгоритмами, а просто заполнить поля vb_login_username и vb_login_password.

$login = "MyUser"
$pass = "Password"
$url = "http://forum.oszone.net"
$r = Invoke-WebRequest $url -SessionVariable oz
$form = $r.Forms[1]
$form.Fields["vb_login_username"] = $login
$form.Fields["vb_login_password"] = $pass
$r = Invoke-WebRequest -Uri ("$url/" + $form.Action)`
    -WebSession $oz -Method POST -Body $form.Fields
$r = Invoke-WebRequest -Uri $url -WebSession $oz
$r.ParsedHtml.Body.OuterText.Split("`r`n") -match "Ваш последний визит"

 

В конечном итоге получим вывод:

output

 

Read Full Post »

Что нам потребуется:

  • Аккаунт на gmail.com
  • Включенный Imap(Настройки – Пересылка и POP/IMAP)

image

  • Скачать библиотеку ImapX — https://imapx.codeplex.com за авторством Павла Азанова – за что ему большая благодарность

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

Add-Type -Path "C:\IMAPX\v4.5\InterIMAP.dll"

1) Подключение к почтовому серверу Imap

ImapX позволяет указать сервер, порт и шифрование тремя различными способами:

1. С помощью одного из параметризованных конструкторов при инициализации нового ImapClient

2. Используя свойства клиента

3. Настройка параметров подключения прямо при вызове метода Connect


Инициализация клиента:

#Без SSL, порт по умолчанию (143) для соединения
$client= New-Object ImapX.ImapClient("imap.gmail.com") 

#С поддержкой SSL, порт по умолчанию (993) для соединения
$client = New-Object ImapX.ImapClient("imap.gmail.com",$true) 
#С поддержкой SSL, порт по умолчанию (993) для соединения, 
#    отключить проверку сертификата
$client = New-Object ImapX.ImapClient("imap.gmail.com",$true,$false)  

#Без SSL, с указанием порта для соединения
$client = New-Object ImapX.ImapClient("imap.gmail.com",888) 

#С поддержкой SSL, с указанием порта для соединения
$client = New-Object ImapX.ImapClient("imap.gmail.com",888,$true) 

#С поддержкой SSL, с указанием порта для соединения, 
#    отключить проверку сертификата
$client = New-Object ImapX.ImapClient("imap.gmail.com",888,$true,$false) 

#С поддержкой TLS, с указанием порта для соединения, 
#    отключить проверку сертификата
$client = New-Object ImapX.ImapClient("imap.gmail.com",888,
    [System.Security.Authentication.SslProtocols]::Tls,$false) 

Подключение:

if($client.Connect( # опциональные параметры # )) {# подключение прошло успешно}
else {
     # подключиться не удалось
}

Какие параметры можно передать методу Connect:

bool Connect()                                                                                                                                                           
bool Connect(string host, bool useSsl, bool validateServerCertificate)                                                                                                   
bool Connect(string host, int port, bool useSsl, bool validateServerCertificate)                                                                                         
bool Connect(string host, int port, 
    System.Security.Authentication.SslProtocols sslProtocol, 
    bool validateServerCertificate) 

2) Аутентификация

ImapX – поддерживает два встроенных метода: логин — пароль и OAuth2.

# Используя связку Логин - Пароль
$client = New-Object ImapX.ImapClient("imap.gmail.com","993",$true,$true)

if($client.Connect()) {  
    if($client.Login("address@gmail.com", "password")) {
        # подключение прошло успешно    }
}
else {
    # подключиться не удалось
}
# Используя OAuth2
$client = New-Object ImapX.ImapClient("imap.gmail.com","993",$true,$true)

if($client.Connect()) {   
    $credentials = New-Object`
        ImapX.Authentication.OAuth2Credentials("login", "token")

    if($client.Login(credentials)) {
        # подключение прошло успешно     
}
}
else {
    # подключиться не удалось
}

3) Работа с папками

В Gmail папки не используются,вместо них есть ярлыки,но будем считать папка = ярлык.

Для доступа к папкам воспользуемся свойством Folders(для нашего примера $client.Folders) класса ImapX.ImapClient.

Просмотр общих папок:

ImapX обеспечивает прямой доступ ко всем общим папкам как Inbox, Drafs, Trash и другие. Учтите, что это не всегда возможно определить эти папки, так как не все сервера предоставляют необходимую информацию .

Общие папки по умолчанию:

PS >  [ImapX.Collections.CommonFolderCollection].GetProperties().Name

All

Archive

Inbox

Drafts

Important

Flagged

Junk

Sent

Для выбора определенной папки,можно воспользоваться индексом массива или методом Item:

# Просмотр папок почтового ящика
PS > $client.Folders.Name
INBOX
[Gmail]
Личные
Путешествие
Работа
Счета

# Выберем папку вся почта
PS > $client.Folders.All

Exists                : 6
Recent                : 0
Recents               : 0
Unseen                : 0
SubFolders            : {}
Messages              : {}
GMailThreads          : {}
HasChildren           : False
Name                  : Вся почта
Selectable            : True
Path                  : [Gmail]/&BBIEQQRP- &BD8EPgRHBEIEMA-
FolderPath            : [Gmail]/&BBIEQQRP- &BD8EPgRHBEIEMA-
Flags                 : {\HasNoChildren, \All}
AllowedPermanentFlags : {}
UidNext               : 1357
UidValidity           : 624695359
SubFolder             : 

# Выберем папку входящая почта
$client.Folders.Inbox
$client.Folders.Item("INBOX")
$client.Folders[0]
$client.Folders["INBOX"]
# Просмотр подпапок(свойство SubFolders)
PS > $client.Folders.Item("[Gmail]").SubFolders.Name
Важное
Вся почта
Корзина
Отправленные
Помеченные
Спам
Черновики

Создание папок:

# Создадим папку -  Новости
PS > $client.Folders.Add("Новости")

Exists                : 0
Recent                : 0
Recents               : 0
Unseen                : 0
SubFolders            : {}
Messages              : {}
GMailThreads          : {}
HasChildren           : False
Name                  : Новости
Selectable            : True
Path                  : &BB0EPgQyBD4EQQRCBDg-
FolderPath            : &BB0EPgQyBD4EQQRCBDg-
Flags                 : {}
AllowedPermanentFlags : {}
UidNext               : 1
UidValidity           : 624695382
SubFolder             : 

# Создадим подпапку BBC в разделе Новости
$client.Folders["Новости"].CreateFolder("BBC")

или

$client.Folders["Новости"].SubFolders.Add("BBC")

PS > $client.Folders["Новости"].SubFolders.Add("BBC")

Exists                : 0
Recent                : 0
Recents               : 0
Unseen                : 0
SubFolders            : {}
Messages              : {}
GMailThreads          : {}
HasChildren           : False
Name                  : BBC
Selectable            : True
Path                  : &BB0EPgQyBD4EQQRCBDg-/BBC
FolderPath            : &BB0EPgQyBD4EQQRCBDg-/BBC
Flags                 : {}
AllowedPermanentFlags : {}
UidNext               : 1
UidValidity           : 624695384
SubFolder             : 

Переименование папок:

# Для переименования достаточно изменить свойство Name

PS  >  $client.Folders[«Новости«].Name =«Кино«

 

Удаление папок:

# Удалим папку Кино
PS > $client.Folders["Кино"].Remove()
True

Для уменьшения обращений  к серверу, чтобы оптимизировать работу,можно воспользоваться :

Lazy или Full

По умолчанию ImapX загрузит два уровня  дерева папок при обращении к свойству Folders. Вложенные папки, которые глубже —  по требованию. Если вы хотите получить  всю структуру папок при первом запросе,  измените поведение клиента:

$client.Behavior.FolderTreeBrowseMode = «Full»

Автоматическая проверка папок

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

$client.Behavior.ExamineFolders = $false

4) Работа с сообщениями

ImapX позволяет выбрать способ, как запрашиваются сообщения. Вы можете установить режим загрузки глобально или индивидуально для поиска или одного сообщении.

Определить глобальный тип загрузки для сообщений:

По умолчанию, глобальный режим загрузки установлен на Basic. В этом режиме запрашиваются флаги, заголовки, структуру тела, размер, внутреннюю дату и тело сообщения (за исключением встроенных изображений и вложений).

# Возможные значения свойства — MessageFetchMode

PS > [enum]::GetNames("ImapX.Enums.MessageFetchMode")
Flags
InternalDate
Size
Headers
BodyStructure
Tiny
Minimal
Body
Basic
Attachments
Full
GMailMessageId
GMailThreads
GMailLabels
GMailExtendedData
None
ClientDefault

$client.Behavior.MessageFetchMode = «Basic»

MessageFetchMode.Tiny — флаги,заголовки и структура тела письма

MessageFetchMode.Minimal — флаги,заголовки,структура тела письма,размер и внутреннюю дату

MessageFetchMode.Basic — флаги,заголовки,структура тела и тело письма,размер и внутреннюю дату

MessageFetchMode.Full — флаги,заголовки,тело письма,вложения,размер и внутреннюю дату

Доступ к  сообщениям:

По умолчанию коллекция Messages  объекта Folder будет пустым. Для загрузки сообщений, воспользуемся метод Download:

# Загрузим все сообщения из папки All
$client.Folders.All.Messages.Download()

# Загрузим первые 3 сообщения из папки All
$client.Folders.All.Messages.Download("All",-1,3)
# Загрузим 1 сообщения из папки Inbox от YouTube
$client.Folders.Inbox.Messages.Download('FROM "YouTube"',-1,1)

Если вы не хотите вызвать метод Download  для каждой папки, можно просто задать поведение клиента для автоматического заполнения коллекции Folder.Messages.

$client.Behavior.AutoPopulateFolderMessages = $true

Запрос сообщений используя  метод Folder.Search:

Также можно использовать метод Folder.Search для запроса сообщений. Все найденные сообщения будут возвращены методом Search будут  добавлены к коллекции Folder.Messages. Метод Messages.Download() вызывает метод Folder.Search, поэтому оба метода принимают одинаковые параметры.

PS > $client.Folders.Inbox.Search

OverloadDefinitions
——————-
ImapX.Message[] Search(string query, ImapX.Enums.MessageFetchMode mode, int count)
ImapX.Collections.MessageCollection Search(string path, bool makeProcess) – Является устаревшим и не испольуется

# Найти все сообщения в папке INBOX
$client.Folders.Inbox.Search()

# Найти первое сообщение в папке INBOX
$client.Folders.Inbox.Search(1)

# Найти пятое сообщение в папке INBOX
$client.Folders.Inbox.Search(5)

# Найти первые пять сообщений в папке INBOX( 0x19 - MessageFetchMode.Tiny)
$client.Folders.Inbox.Search("All",0x19,5)

Для формирования фильтра,более подробно можно прочитать в RFC 3501 пункт 6.4.4.

# Найти все сообщения в папке INBOX
$client.Folders.Inbox.Search()

# Найти первое сообщение в папке INBOX
$client.Folders.Inbox.Search(1)

# Найти пятое сообщение в папке INBOX
$client.Folders.Inbox.Search(5)

# Найти первые пять сообщений в папке INBOX( 0x19 - MessageFetchMode.Tiny)
$client.Folders.Inbox.Search("All",0x19,5)

# Найдем сообщения, где в теле письма(BODY) есть YouTube
$client.Folders.Inbox.Search('BODY "YouTube"',0x19,-1)

# Найдем все сообщения от YouTube
$client.Folders.Inbox.Search('FROM "YouTube"')

# Найдем сообщения первое сообщение от YouTube
$client.Folders.Inbox.Search('FROM "YouTube"',0x19,1)

date            = date-day "-" date-month "-" date-year
date-day        =1*2DIGIT
date-month      ="Jan"/"Feb"/"Mar"/"Apr"/"May"/"Jun"/"Jul"/"Aug"/"Sep"/"Oct"/"Nov"/"Dec"
# Найдем сообщения полученные от 1 декабря 2013 года
$client.Folders.Inbox.Search('SINCE "01-DEC-2013"')

# Найдем сообщения полученные  30 ноября 2013 года
$client.Folders.Inbox.Search('ON "30-NOV-2013"') 

# Найдем сообщения в тему у которых есть слово Windows
$client.Folders.Inbox.Search('SUBJECT "Windows"')

Скачивание вложений:

# Просмотр вложений
$m.Attachments

# Имя файла
$m.Attachments.ContentType.Name

# Прежде,чем сохранить или работать с Steam,требуется загрузить Download()
$m.Attachments.Download()

# Проверить загрузку объекта
$m.Attachments.Downloaded

# Сохраним вложение
$m.Attachments.Save("C:\files",$m.Attachments.ContentType.Name)

Копирование сообщений:

# Скопируем сообщение в папку Trash
$m=$client.Folders.Trash.Search('FROM "test@xxx.ru"')
$m[0].CopyTo($client.Folders.Trash,$true)

Удаление сообщений:

# Найсти все сообщений от YouTube и удалить
$client.Folders.Inbox.Search('FROM "YouTube"') | 
    ForEach-Object {$_.Remove()}

Работа с флагами:

# Флаги сообщения
$m=$client.Folders.Trash.Search('FROM "test@xxx.ru"')
$m.Flags

# Добавим флаг \SEEN
$m=$client.Folders.Trash.Search('FROM "test@xxx.ru"')
$m.Flags.Add("\SEEN")

# Удалим флаг \SEEN
$m=$client.Folders.Trash.Search('FROM "test@xxx.ru"')
$m.Flags.Remove("\SEEN")

Экспорт сообщений:

# Экспорт сообщения в файл
$m=$client.Folders.Inbox.Messages[0]
$m.Save("C:\message.eml")
$m.SaveTo("C:\","message.eml")

5) Отключение от почтового сервера Imap

# Disconnect
$client.Disconnect()
Remove-Variable -Name client

PS. Если Вам понравилась библиотека ImapX, Вы можете выразить благодарность Павлу Азанову:

Donate

Read Full Post »

По мотивам — Powershell и Google Drive API (Загрузить файл на гугл диск)

1) Скачиваем и устанавливаем Google Data APIs (GData)

2) Создаем аккаунт Google, если отсутствует  — https://accounts.google.com 

3) Переходим на сайт  https://cloud.google.com/console

4) Создаем новое или выбираем существующий проект

project

5) Переходим в меню APIs & auth  -> APIs

6) Включаем нужные API (Drive API и Drive SDK) в значение ON

api

7) Переходим в меню APIs & auth  -> Registered apps и нажать Register app

8) Указать название приложения(для нашего примера Test) и выбрать тип Native

regapp

9) Выбираем наше приложение и выбираем пункт Update(требуется указать почтовый адрес и название нашего приложения)

native

content

Воспользуемся примером кода «Uploading a new document or file with both metadata and content«.

#Загружаем требуемые библиотеки(путь может быть изменен,Program Files)
Add-Type -Path "C:\Program Files (x86)\Google\Google Data API SDK\Redist\Google.GData.Documents.dll"
Add-Type -Path "C:\Program Files (x86)\Google\Google Data API SDK\Redist\Google.GData.Client.dll"

#Учетные данные Google Account
$glogin = 'Login'
$gpass = 'password'

#Файл для загрузки
$FileToUpload = "C:\PDF\wp__understanding-wmi-malware.pdf"
# Имя файла без расширения
$fname = [System.IO.Path]::GetFileNameWithoutExtension($FileToUpload)

#Указываем API, отделяя их запятой,список - https://support.google.com/a/answer/162106?hl=ru
$scopes = "https://docs.google.com/feeds/ https://docs.googleusercontent.com/"

#Подтавляем данные из пункта №9
$param = New-Object Google.GData.Client.OAuth2Parameters -Property @{
	ClientId = "ID"
	ClientSecret = "Secret"
	RedirectUri = "urn:ietf:wg:oauth:2.0:oob"
	Scope = $scopes
}

#Получаем ссылку для авторизации OAuth
$url = [Google.GData.Client.OAuthUtil]::CreateOAuth2AuthorizationUrl($param)

#После перехода по ссылке получаем код
$ie = New-Object -ComObject InternetExplorer.Application
$ie.Navigate($url)
While ($ie.busy -eq $true)
      {Start-Sleep -Milliseconds 500}
While ($ie.Document.Title -match "не подключены")
      {Start-Sleep -Milliseconds 500}
If ($ie.Document.Title -match "Вход"){
    $login=$ie.document.getElementByID("Email")
    $login.Value=$glogin
    $pass=$ie.document.getElementByID("Passwd")
    $pass.Value=$gpass
    $ie.document.getElementByID("signIn") | Foreach {$_.click()}
    While ($ie.busy -eq $true)
            {Start-Sleep -Milliseconds 500}
}

If ($ie.Document.Title -match "Запрос на разрешение")
{
    $ie.Document.Forms.Item(0) | where {$_.innerText -eq "Принять"} | Foreach {$_.click()}
    While ($ie.busy -eq $true)
            {Start-Sleep -Milliseconds 500}
    If($ie.Document.Body.outerHTML -match 'value="(.+)"><script')
            {$global:param.AccessCode = $matches[1]}
 }
 $ie.Quit()

#Получаем токен авторизации и создаем аутентификатор
[Google.GData.Client.OAuthUtil]::GetAccessToken($param)
$authenticator = New-Object Google.GData.Client.OAuth2Authenticator("Test",$param)
$rf = new-object Google.GData.Client.GOAuth2RequestFactory("apps", "Test", $param)

$service = New-Object Google.GData.Documents.DocumentsService("Test")
$service.RequestFactory = $rf

#Формируем документ на отправку
$dentry=New-Object Google.GData.Documents.DocumentEntry
$dentry.Title.Text="$fname"
$dentry.MediaSource=New-Object Google.GData.Client.MediaFileSource($FileToUpload,"application/pdf")
$createUploadUrl=New-Object System.Uri("https://docs.google.com/feeds/upload/create-session/default/private/full")
$link=New-Object Google.GData.Client.AtomLink($createUploadUrl)
$link.Rel = [Google.GData.Client.ResumableUpload.ResumableUploader]::CreateMediaRelation
$dentry.Links.Add($link)

$dentry.Service=$service

#Загрузка файла
$uploader=New-Object Google.GData.Client.ResumableUpload.ResumableUploader
Register-ObjectEvent -InputObject $uploader -EventName AsyncOperationProgress -Action {
	Write-Progress -activity Upload -status 'Progress->' -percentcomplete $EventArgs.ProgressPercentage} | Out-Null
Register-ObjectEvent -InputObject $uploader -EventName AsyncOperationCompleted -Action {
	Get-EventSubscriber | Unregister-Event -Force
	Write-Host -ForegroundColor Yellow "Completed!!!"
} | Out-Null
$uploader.InsertAsync($authenticator, $dentry, (new-object object))

Read Full Post »

1) Как установить  PowerShell V4?

.NET Framework 4.5 (В состав Windows 8.1 и Windows Server 2012,Windows Server 2012 R2 входит .NET Framework 4.5. Поэтому установка этого программного обеспечения в указанных операционных системах не требуется.)  должен быть установлен перед установкой Windows Management Framework 4.0 (WMF).

PowerShell является частью пакета Windows Management Framework 4.0 (WMF) и может быть скачан по следующим ссылкам:

Windows Management Framework 4.0 (WMF)  нельзя установить на  Windows 8, поэтому следует обновиться до Windows 8.1.

WMF 3.0  и Windows PowerShell 2.0 engine не требуются для установки WMF 4.0. Однако, Windows PowerShell 2.0 engine может быть установлен отдельно, используя Server Manager или Control Panel,  для тестирования сценариев,которые используют функциональность Windows PowerShell 2.0 (Powershell.exe -Version, Start-Job -PSVersion, and Register-PSSessionConfiguration –PSVersion).  При установленном WMF 3.0  и Windows PowerShell 2.0 engine , можно выбрать для запуска Windows PowerShell 2.0 или Windows PowerShell 3.0. После установки WMF 4.0,можно работать с  Windows PowerShell 2.0 или Windows PowerShell 4.0. Windows PowerShell 4.0 обратно совместим с Windows PowerShell 3.0, за исключением указанных в разделе Breaking Changes документа WINDOWS MANAGEMENT Framework 4.0 Release Notes.

Windows PowerShell V4 несовместим:

  • System Center 2012 Configuration Manager (not including SP1)
  • System Center Virtual Machine Manager 2008 R2 (including SP1)
  • Microsoft Exchange Server 2013, Microsoft Exchange Server 2010, and Microsoft Exchange Server 2007
  • Microsoft SharePoint 2013 and Microsoft SharePoint 2010
  • Windows Small Business Server 2011 Standard

Полезные ссылки по установке и запуску Windows PowerShell:

2) Какие компоненты были обновлены?

  • Windows PowerShell
  • Windows PowerShell Integrated Scripting Environment (ISE)
  • Windows PowerShell Web Services (Management OData IIS Extension)
  • Windows Remote Management (WinRM)
  • Windows Management Instrumentation (WMI)

3) Какие новые компоненты были добавлены?

  • Windows PowerShell Desired State Configuration (DSC)

4) Версионность PowerShell

Изменения:

  • Значение переменной $PSVersionTable.PSVersion  обновлено до  4.0

PS >  $PSVersionTable.PSVersion

Major  Minor  Build  Revision
——  ——  ——  ———
4      0      -1     -1

  • Изменен баннер copyright

Windows PowerShell
Copyright (C) 2013 Microsoft Corporation. All rights reserved.

  • Версия  Windows PowerShell 4.0 заменяет версию 3.0. Запросы для загрузки версии 3.0 автоматически загружают версию 4.0. Например,  Powershell.exe –Version 3.0 автоматически загружает версию 4.0
  • Session configurations  автоматически загружает версию 4.0, вместо 3.0
  • Командлеты использующие при запуске определенную версию Windows PowerShell, автоматически используют версию 4.0, если указана версия 3.0.  Такие  командлеты,как  Start-Job, Set-PSSessionConfiguration, и Register-PSSessionConfiguration с параметром -PSVersion <Version>
  • Значение ключа реестра HKLM:\SOFTWARE\Microsoft\PowerShell\3\PowerShellEngine\PowerShellVersion изменно с  3.0 на 4.0

PS  >  Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\PowerShell\3\PowerShellEngine -Name PowerShellVersion

PowerShellVersion : 4.0

  • Значение ключа реестра HKLM:\SOFTWARE\Microsoft\PowerShell\3\PowerShellEngine\PSCompatibleVersion изменно с  1.0, 2.0, 3.0 на 1.0, 2.0, 3.0, 4.0

PS >  Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\PowerShell\3\PowerShellEngine -Name PSCompatibleVersion

PSCompatibleVersion : 1.0, 2.0, 3.0, 4.0

Не измененные значения:

  • Переменная $pshome ссылается на  %windir%\system32\WindowsPowerShell\v1.0
  • Расширение скриптов Windows PowerShell имеет значение  .ps1
  • Значение ключей реестра Windows PowerShell расположены по адресу  HKLM:\SOFTWARE\Microsoft\PowerShell\1 и HKLM:\SOFTWARE\Microsoft\PowerShell\3

5)  Execution Policy по умолчанию в Windows Server 2012 R2?

Значение Execution Policy по умолчанию в Windows Server 2012 R2 установлено в RemoteSigned. Execution Policy для Windows 8.1 по умолчанию Restricted.

6) Новый параметр –IncludeUserName типа switch у командлета Get-Process

Данный параметр позволяет получить список пользователей от которых запущен процесс. Данный параметр требует запускать с повышенными привилегиями.

PS >  Get-Process -IncludeUserName
Get-Process : The ‘IncludeUserName’ parameter requires elevated user rights. Try running the command again in a session that has been opened with elevated user rights (that is, Run as Administrator).

Пример:

PS >  Get-Process -IncludeUserName | Where UserName -match System | Format-Table Name,UserName

Name          UserName
—-          ———
armsvc    NT AUTHORITY\SYSTEM
csrss         NT AUTHORITY\SYSTEM
csrss         NT AUTHORITY\SYSTEM

Для более ранних версий Windows PowerShell:

7) Новый командлет(функция) Get-FileHash

PS  >  (Get-Command Get-FileHash).CommandType
Function

Посмотреть код функции: — ${function:Get-FileHash}

Используя командлет Get-Member или исходный код функции,можно увидеть,что функция имеет тип retVal.psobject.TypeNames.Insert(0, «Microsoft.Powershell.Utility.FileHash»).

Также можно посмотреть,какое форматирование использует данная функция.

PS >  (Get-FormatData Microsoft.Powershell.Utility.FileHash).FormatViewDefinition.Control

Name                                                        Control
—-                                                        ——-
Microsoft.Powershell.Utility.FileHash                       TableControl

Далее можно посмотреть столбцы по умолчанию:

PS >  (Get-FormatData Microsoft.Powershell.Utility.FileHash).FormatViewDefinition.Control.Rows.Columns

Alignment DisplayEntry
——— ————
Undefined Algorithm
Undefined Hash
Undefined Path

Параметр Algorithm поддерживает следующие параметры:

PS >  (Get-Command Get-FileHash).Parameters[«Algorithm»].Attributes.ValidValues
SHA1
SHA256
SHA384
SHA512
MACTripleDES
MD5
RIPEMD160

Примеры:

PS >  Get-FileHash $pshome\powershell.exe | Format-List

Algorithm : SHA256
Hash      : E9FA973EB5AD446E0BE31C7B8AE02D48281319E7F492E1DDAADDDFBDD5B480C7
Path      : C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe

PS >  Get-FileHash $pshome\powershell.exe -Algorithm Sha1| Format-List

Algorithm : SHA1
Hash      : 10A3671C0FBC2BCE14FC94891E87E2F4BA07E0DF
Path      : C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe

Для более ранних версий Windows PowerShell:

  • Модуль PSCX и командлет Get-Hash
  • Скрипт на .Net под авторством Boe Prox — Get Hashes of Files
  • Скрипт на .Net под авторством Lee Holmes — Get-FileHash.ps1

8) Переименовали параметр Workgroup у командлета Remove-Computer

В PowerShell 3.0  командлету Remove-Computer добавили параметр Workgroup(позволяет указать имя рабочей группы,после отсоединения компьютера из домена). В PowerShell 4.0 переименовали данный параметр с Workgroup на WorkgroupName  ,теперь оба командлета Add-Computer и Remove-Computer используют единый параметр –WorkgroupName.

9) Новый параметр FullyQualifiedName у командлета Get-Module

В PowerShell 4.0 командлету Get-Module добавили новый параметр типа ModuleSpecification[].

Формат параметра  —  @{ModuleName = «modulename»; ModuleVersion = «version_number»;GUID=»guid»}.  ModuleName и ModuleVersion(желательно придерживаться формата n.n.n.n) – обязательные.

Пример:

PS >  Get-Module -FullyQualifiedName @{ModuleName=»AppLocker»;ModuleVersion=»1.0.0.0″} –ListAvailable

PS >  Get-Module -Name AppLocker -ListAvailable | Format-Table Name,Version,GUID -AutoSize

Name      Version Guid
—-      ——- —-
AppLocker 1.1.0.0 27807c40-84db-4998-ae01-11319de093ea
AppLocker 1.0.0.0 9dafd409-67de-4108-8ee9-73cd61f5b7bf

PS >  Get-Module -FullyQualifiedName @{ModuleName=»AppLocker»;ModuleVersion=»1.0.0.0″;GUID=»27807c40-84db-4998-ae01-11319de093ea»} –ListAvailable

10) Новый параметр PassThru у командлетов Enable-JobTrigger и Disable-JobTrigger

Параметр Passthru отображает все объекты, созданные или измененные с помощью командлета.

Пример:

PS >  Get-ScheduledJob | Get-JobTrigger

Id         Frequency       Time                   DaysOfWeek              Enabled
—         ———       —-                   ———-              ——-
1          Weekly          11.11.2013 23:00:00    {Monday, Wednesday, … True

PS >  Get-ScheduledJob | Get-JobTrigger | Disable-JobTrigger -PassThru

Id         Frequency       Time                   DaysOfWeek              Enabled
—         ———       —-                   ———-              ——-
1          Weekly          11.11.2013 23:00:00    {Monday, Wednesday, … False

11) Новый параметр RepeatIndefinitely у командлетов New-JobTrigger и Set-JobTrigger

Параметр RepeatIndefinitely позволяет избавиться от использования конструкции вида -RepetitionDuration ([TimeSpan]::MaxValue), которая служит для неоднократного запуска запланированного задания на неопределенный срок.

Пример:

PS >  New-JobTrigger -Once -At «1/11/2013» -RepetitionInterval (New-TimeSpan -Hour 2) -RepetitionDuration ([TimeSpan]::MaxValue)

Теперь команду можно записать:

PS >  New-JobTrigger -Once -At «1/11/2013» -RepetitionInterval (New-TimeSpan -Hour 2) -RepeatIndefinitely

Команда создает job trigger ,который запускает раз в 2 часа на неопределенный срок. Старт задачи 1/11/2013 – 0:00:00.

12) Новый параметр RunNow у командлетов Register-ScheduledJob и Set-ScheduledJob

Параметр RunNow позволяет не указывать параметр trigger для job для немедленного  выполнения задачи без указания даты и времени.

Пример:

PS >  Register-ScheduledJob -Name Test-FileName -FilePath C:\Scripts\TestFile.ps1 –RunNow

13) Изменения в командлете Save-Help?

Командлет Save-Help теперь позволяет сохранить справку для модулей(modules), которые установленные на удаленных компьютерах. В PowerShell 3.0 командлет Save-Help позволял работать только с локальным компьютером. Хотя есть возможность экспортировать модуль с удаленного компьютера,используя Windows PowerShell remoting и получить ссылку на объект PSModuleInfo, свойство HelpInfoUri не сохраняется. В PowerShell 4.0 свойство HelpInfoUri сохраняется, что позволяет Save-Help работать с удаленными модулями. Также возможно сохранить объект PSModuleInfo на диск или внешний накопитель используя командлет Export-CliXml  на компьютере без доступа к интернету, импортировать объект на компьютере с интернетом и запустить Save-Help на десериализованном объектее PSModuleInfo. Сохраненный файлы помощи можно перенести на исходный компьютер и обновить справку с помощью командлета Update-Help.

Пример:

Example 1: Run Save-Help to save the help for the DhcpServer module from an Internet-connected client computer, without installing the DhcpServer module or DHCP Server role on the local computer.

# Option 1: Run Invoke-Command to get the remote module and call Save-Help

$m = Invoke-Command -ComputerName RemoteServer -ScriptBlock { Get‑Module ‑Name DhcpServer -ListAvailable }

Save-Help -Module $m -DestinationPath C:\SavedHelp

# Option 2: Use a PSSession to get the remote module and call Save-Help

$s = New-PSSession -ComputerName RemoteServer

$m = Get-Module -PSSession $s -Name DhcpServer -ListAvailable

Save-Help -Module $m -DestinationPath C:\SavedHelp

# Option 3: Use a CimSession to get the remote module and call Save-Help

$c = New-CimSession -ComputerName RemoteServer

$m = Get-Module -CimSession $c -Name DhcpServer -ListAvailable

Save-Help -Module $m -DestinationPath C:\SavedHelp

Example 2: Install help for the DhcpServer module on a computer without any network access.

# Run Export-CliXml to serialize the PSModuleInfo object to disk or removable media

$m = Get-Module -Name DhcpServer –ListAvailable

Export-CliXml –Path E:\UsbFlashDrive\DhcpModule.xml –InputObject $m

# Transport the removable media to a computer with Internet access and import with Import-CliXml

$deserialized_m = Import-CliXml E:\UsbFlashDrive\DhcpModule.xml

Save-Help -Module $deserialized_m -DestinationPath E:\UsbFlashDrive\SavedHelp

# Transport the removable media back to the computer without network access and install the help

Update-Help –Module DhcpServer –SourcePath E:\UsbFlashDrive\SavedHelp

Read Full Post »

NetSh vs NETTCPIP модуль на простой задаче

1) Посмотреть сетевые интерфейсы в системе
netsh int ipv4 show int

Idx     Met         MTU          State                Name
—  ———-  ———-  ————  —————————
1          50  4294967295  connected     Loopback Pseudo-Interface 1
12          10        1500  connected     Ethernet

2) Сконфигурируем статический адрес
netsh int ipv4 set address «Ethernet» static 192.168.100.100 255.255.255.0 192.168.100.1

PS. В отличии от командлета New-NetIPAddress утилита netsh не проверяет из какой подсети шлюз
netsh int ipv4 set address «Ethernet» static 192.168.100.100 255.255.255.0 10.10.10.1

IPv4 Address. . . . . . . . . . . : 192.168.100.100
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . : 10.10.10.1

3) Изменим шлюз по умолчанию
netsh int ipv4 set address «Ethernet» static 192.168.100.100 255.255.255.0 192.168.100.2

IPv4 Address. . . . . . . . . . . : 192.168.100.100
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . : 192.168.100.2

4) Удалим шлюз по умолчанию
netsh int ipv4 set address «Ethernet» static 192.168.100.100 255.255.255.0

IPv4 Address. . . . . . . . . . . : 192.168.100.100
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . :

5) Изменим со статического адреса на dhcp
netsh int ipv4 set address «Ethernet» dhcp

IPv4 Address. . . . . . . . . . . : 192.168.0.26
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . : 192.168.0.1

С утилитой netsh проблем не возникает.

Теперь попробуем произвести подобные манипуляции с модулем NETTCPIP.

1) Посмотреть сетевые интерфейсы в системе
Get-NetAdapter

Name                       : Ethernet
InterfaceIndex             : 12
InterfaceOperationalStatus : Up
AdminStatus                : Up
LinkSpeed(Gbps)            : 1
MediaConnectionState       : Connected
ConnectorPresent           : True

2) Убедимся,что адрес получен через DHCP
Get-NetIPConfiguration

InterfaceAlias       : Ethernet
InterfaceIndex       : 12
IPv4Address          : 192.168.0.26
IPv4DefaultGateway   : 192.168.0.1
DNSServer            : 192.168.0.96

3) Сконфигурируем статический адрес на интерфейсе с номером 12
New-NetIPAddress –InterfaceIndex 12 –IPAddress 192.168.100.100 -PrefixLength 24 -DefaultGateway 192.168.100.1

InterfaceAlias       : Ethernet
InterfaceIndex       : 12
IPv4Address          : 192.168.100.100
IPv4DefaultGateway   : 192.168.100.1
DNSServer            : 192.168.0.96

PS. Если мы укажем шлюз не из той подсети,то получим ошибку:
New-NetIPAddress –InterfaceIndex 12 –IPAddress 192.168.100.100 -PrefixLength 24 -DefaultGateway 10.10.10.1

New-NetIPAddress : DefaultGateway 10.10.10.1 is not on the same network segment (subnet) that is defined by the IP addr
ess 192.168.100.100 and PrefixLength 24.
At line:1 char:1
+ New-NetIPAddress -InterfaceIndex 12 -IPAddress 192.168.100.100 -PrefixLength 24  …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidArgument: (MSFT_NetIPAddress:ROOT/StandardCimv2/MSFT_NetIPAddress) [New-NetIPAddr
ess], CimException
+ FullyQualifiedErrorId : Windows System Error 87,New-NetIPAddress

4) Изменим шлюза по умолчанию

1) Remove-NetIPAddress&New-NetIPAddress

С этим уже сложнее,т.к. командлет Set-NetIPAddress не содержит параметр для измения шлюза по умолчанию.

a) Удалим ранее установленные параметры
    Remove-NetIPAddress -InterfaceIndex 12 -DefaultGateway 192.168.100.1 -Confirm:$false
b) Установим коректные значения
New-NetIPAddress –InterfaceIndex 12 –IPAddress 192.168.100.100 -PrefixLength 24 -DefaultGateway 192.168.100.2

PS. Но и тут не без сюрприза,если установить шлюз из другой подсети(например 10.10.10.1),
то шлюз по умолчанию удалить с помощью Remove-NetIPAddress не получится,пока не назначим адрес из подсети шлюза.
При использовании Remove-NetRoute проблем не возникает.

IPv4DefaultGateway   : 10.10.10.1

2) New-NetRoute&Remove-NetRoute
a) Добавим новый шлюз
New-NetRoute -InterfaceIndex 12 -NextHop 192.168.100.2 -DestinationPrefix 0.0.0.0/0

InterfaceAlias       : Ethernet
InterfaceIndex       : 12
IPv4Address          : 192.168.100.100
IPv4DefaultGateway   : {192.168.100.2, 192.168.100.1}
DNSServer            : 192.168.0.96

b) Удалим старый шлюз
  Remove-NetRoute -InterfaceIndex 12 -NextHop 192.168.100.1 -Confirm:$false

InterfaceAlias       : Ethernet
InterfaceIndex       : 12
IPv4Address          : 192.168.100.100
IPv4DefaultGateway   : 192.168.100.2
DNSServer            : 192.168.0.96

5) Удалим шлюз по умолчанию
1) Используя Remove-NetRoute
a) Remove-NetRoute -InterfaceIndex 12 -NextHop 192.168.100.1 -Confirm:$false
InterfaceAlias       : Ethernet
InterfaceIndex       : 12
IPv4Address          : 192.168.100.100
IPv4DefaultGateway   :
DNSServer            : 192.168.0.96
b) Remove-NetRoute -InterfaceIndex 12 -DestinationPrefix 0.0.0.0/0 -Confirm:$false
Позволяет удалить,когда больше 2-ух шлюзов по умолчанию

2) Используя Remove-NetIPAddress
a) Удалим ранее установленные параметры
Remove-NetIPAddress -InterfaceIndex 12 -DefaultGateway 192.168.100.1 -Confirm:$false
b) Установим коректные значения
New-NetIPAddress –InterfaceIndex 12 –IPAddress 192.168.100.100 -PrefixLength 24

6) Изменим со статического адреса на dhcp
Set-NetIPInterface -InterfaceIndex 12 -Dhcp Enabled

PS. В этом случае остается шлюз по умолчанию,что нам совершенно не нужно.

New-NetIPAddress -InterfaceIndex 12 -IPAddress 192.168.100.100 -PrefixLength 24 -DefaultGateway 192.168.100.2

InterfaceAlias       : Ethernet
InterfaceIndex       : 12
IPv4Address          : 192.168.100.100
IPv4DefaultGateway   : {192.168.100.2, 192.168.100.1}
DNSServer            : 192.168.0.96

Теперь у нас два шлюза.
a) Удалим ранее установленные параметры(остается шлюз 192.168.100.2)
   Remove-NetIPAddress -InterfaceIndex 12 -DefaultGateway 192.168.100.1 -Confirm:$false
b) Установим коректные значения
    New-NetIPAddress –InterfaceIndex 12 –IPAddress 192.168.100.100 -PrefixLength 24
с) Удалим теперь второй шлюз
    Remove-NetIPAddress -InterfaceIndex 12 -DefaultGateway 192.168.100.2 -Confirm:$false
d) Установим получать через DHCP
Set-NetIPInterface -InterfaceIndex 12 -Dhcp Enabled

или

Set-NetIPInterface -InterfaceIndex 12 -Dhcp Enabled
    Remove-NetRoute -InterfaceIndex 12 -NextHop 192.168.100.1 -Confirm:$false

Read Full Post »

Вопрос был задан на форуме OsZone.Net в теме «PowerShell — Закрытие всех окон приложений кроме активного« . Был предложен вариант с ярлыком, но возникла проблема, при нажатии hotkey  — фокус приложения переключался на другое приложение.

Что нам потребуется:

Основная проблема – это работа с hotkey. Идея работы позаимствована у Hans Passant’a — How to prevent my C# winforms application from stealing focus when I set the visible properly to true?

Схема скрипта:

  • Функция RegisterHotKey —  определяет комбинацию «горячая» клавиша для текущего потока

RegisterHotKey(this.Handle, MYKEYID, MOD_CONTROL + MOD_SHIFT, Keys.U);

  • this.Handle  — Идентификатор окна нашей формы,которое принимает сообщение WM_HOTKEY
  • MYKEYID — Определяет идентификатор комбинации
  • MOD_CONTROL + MOD_SHIFT — Определяет клавиши, которые должны быть нажаты в комбинации с клавишей, заданной параметром vk, чтобы генерировать сообщение WM_HOTKEY. Параметр modifiers может быть комбинацией следующих значений:
    • MOD_ALT — Любая клавиша ALT должна удерживаться нажатой.
    • MOD_CONTROL — Любая клавиша CTRL должна удерживаться нажатой.
    • MOD_SHIFT — Любая клавиша SHIFT должна удерживаться нажатой.
  • Keys.U — Определяет код виртуальной клавиши комбинации «горячая» клавиша.

В данном примере комбинация будет CTRL+Shift + U

  • Функция  UnregisterHotKey — освобождает комбинацию «горячая» клавиша, предварительно зарегистрированную вызывающим потоком

UnregisterHotKey(this.Handle, MYKEYID);

  • this.Handle  — Идентификатор окна нашей формы,которое принимает сообщение WM_HOTKEY
  • MYKEYID — Определяет идентификатор комбинации

Вызывается при закрытии формы

  • Для обработки сообщений операционной системы в нашей форме, потребуется переопределить метод WndProc.
  • if (m.Msg == WM_HOTKEY && m.WParam.ToInt32() == MYKEYID) { – Производим проверку на получение сочетания CTRL+Shift +
  • Вызов скрипта PowerShell —  How to run PowerShell scripts from C#
  • Import-Module -Name Wasp\r\nSelect-Window | Where-Object { -not $_.IsActive} | Remove-Window
    • Import-Module -Name Wasp – Импортирует модуль WASP (не обязательно для PowerShell V3, но с учетом того,что переменная $PSModuleAutoloadingPreference не None)
    • Select-Window – Получаем список окон
    • Where-Object { -not $_.IsActive} – Отфильтровываем все кроме активного
    • Remove-Window – Закрываем окна
  • Function OnLoad –  Определяем свойства для запуска формы в скрытом режиме
  • Function OnClick – При нажатии на кнопку меню Exit – закрываем форму
  • Создаем значок в трее и определяем меню Exit
  • Запуск формы
$ActiveWindow = @'
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Management.Automation;
using System.Management.Automation.Runspaces;

namespace WindowsApplication1 {
public partial class Form1 : Form {
private const int MYKEYID = 0;
public Form1() {
RegisterHotKey(this.Handle, MYKEYID, MOD_CONTROL + MOD_SHIFT, Keys.U);
this.FormClosing += Form1_FormClosing;
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
UnregisterHotKey(this.Handle, MYKEYID);
}

protected override void SetVisibleCore(bool value)
{
base.SetVisibleCore(false);
}

protected override void WndProc(ref Message m) {
if (m.Msg == WM_HOTKEY && m.WParam.ToInt32() == MYKEYID) {
// create Powershell runspace
Runspace runspace = RunspaceFactory.CreateRunspace();

// open it
runspace.Open();

// create a pipeline and feed it the script text
string scriptText = "Import-Module -Name Wasp\r\nSelect-Window | Where-Object { -not $_.IsActive} | Remove-Window";
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(scriptText);

// execute the script
pipeline.Invoke();

// close the runspace
runspace.Close();
}
base.WndProc(ref m);
}

// P/Invoke declarations
private const int WM_HOTKEY = 0x312;
private const int MOD_ALT = 1;
private const int MOD_CONTROL = 2;
private const int MOD_SHIFT = 4;
[DllImport("user32.dll")]
private static extern int RegisterHotKey(IntPtr hWnd, int id, int modifier, Keys vk);
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
}
}
'@

Function OnLoad {
$form.Visible = $false
$form.ShowInTaskbar = $false
}

Function OnClick {
$trayIcon.Visible = $False
$form.close()
}

Add-Type -TypeDefinition $ActiveWindow -ReferencedAssemblies System.Windows.Forms
Add-Type -AssemblyName System.Drawing

$trayIcon = New-Object System.Windows.Forms.NotifyIcon
$trayMenu = New-Object System.Windows.Forms.ContextMenu
$menuExit = New-Object System.Windows.Forms.MenuItem
$form = New-Object WindowsApplication1.Form1

$trayIcon.Icon = [System.Drawing.SystemIcons]::Information
$trayIcon.ContextMenu = $trayMenu

$menuExit.Text = "Exit"
$menuExit.Add_Click({OnClick})

$trayMenu.MenuItems.AddRange(@($menuExit))

$form.Add_Load({OnLoad})

$trayIcon.Visible = $true
[Windows.Forms.Application]::Run($form)

Запуск скрипта:

Powershell –WindowStyle Hidden –File «C:\Script\Close-InactiveWindow.ps1»

Выход:

Read Full Post »

1) С помощью служебной программы Diskpart с интерфейсом командной строки

Назначение: Windows Xp, Windows Vista, Windows 7, Windows 8, Windows Server 2003, Windows Server 2008, Windows Server 2008 R2, Windows Server 2012

Программа Diskpart позволяет выполнять расширенный набор действий, поддерживаемых оснасткой "Управление дисками". Оснастка "Управление дисками" защищает данные на диске путем блокирования действий, которые могут привести к их повреждению. Будьте внимательны, используя программу Diskpart, поскольку она предоставляет явный контроль над разделами и томами!

Для получение статуса программного массива – воспользуемся командой "list volume":

#Вывод для русской локализации

PS >  "list volume" | diskpart 

DISKPART> 
  
  Том ###     Имя      Метка     ФС     Тип              Размер    Состояние  Сведения    
  ----------  ---  -----------  -----  ----------------  -------  ----------  --------
  Том 0         F   My RAID Set  NTFS   RAID-5            4096 МБ  Исправен
  Том 1         G   FATSTRIPE    FAT32  Чередующийся том  6144 МБ  Неисправен
  Том 2         H   My Mirror    NTFS   Зеркальный том    2048 МБ  Исправен
  Том 3         I   My Span      NTFS   Составной том        9 ГБ  Исправен
  Том 4         D                CDFS   CD-ROM               0 Б
  Том 5         C                NTFS   Раздел            2047 МБ  Исправен    Системный
  Том 6         Е                NTFS   Раздел            2063 МБ  Исправен    Загрузочный
  Том 7         J   My Primary   NTFS   Раздел            4095 МБ  Исправен
  Том 8         K   My Logical   NTFS   Раздел            2047 МБ  Исправен
  Том 9         L   My Next Log  NTFS   Раздел            2534 МБ  Исправен

#Вывод для английской локализации

DISKPART>  
  
  Volume ###  Ltr  Label        Fs     Type              Size     Status      Info    
  ----------  ---  -----------  -----  ----------------  -------  ----------  --------
  Volume 0     F   My RAID Set  NTFS   RAID-5            4096 MB  Healthy
  Volume 1     G   FATSTRIPE    FAT32  Stripe            6144 MB  Failed
  Volume 2     H   My Mirror    NTFS   Mirror            2048 MB  Healthy
  Volume 3     I   My Span      NTFS   Spanned              9 GB  Healthy
  Volume 4     D                CDFS   CD-ROM               0 B
  Volume 5     C                NTFS   Partition         2047 MB  Healthy     System
  Volume 6     E                NTFS   Partition         2063 MB  Healthy     Boot
  Volume 7     J   My Primary   NTFS   Partition         4095 MB  Healthy
  Volume 8     K   My Logical   NTFS   Partition         2047 MB  Healthy
  Volume 9     L   My Next Log  NTFS   Partition         2534 MB  Healthy

 

Более подробно — How Basic Disks and Volumes Work

 

2) Используя VDS (Virtual Disk Services) API

Назначение:  Windows Vista, Windows 7, Windows 8, Windows Server 2003, Windows Server 2008, Windows Server 2008 R2, Windows Server 2012

Microsoft Storage VDS Library — позволяет получить  статус программного массива. Но данная библиотека является недокументированной и неподдерживаемой, хотя и поставляется вместе с ОС. Так что используем на свой страх и риск.

  • Для работы с библиотекой требуются права Администратора
  • При использовании Windows 7,Windows Vista – требуется установить Remote Server Administration Tools (RSAT) и компонент из набора Share and Storage Management Tools

 

 

Перечисления:

  • VDS_VOLUME_STATUS – статус тома
    VDS_VS_UNKNOWN

    The status of the volume is unknown. This value does not apply to dynamic volumes.

    VDS_VS_ONLINE

    The volume is available.

    VDS_VS_NO_MEDIA

    The volume is removable media, such as a CD-ROM.

    VDS_VS_FAILED

    The volume is unavailable.

    VDS_VS_OFFLINE

    The volume is offline.

  • VDS_VOLUME_TYPE – тип тома
    VDS_VT_UNKNOWN

    The volume type is unknown.

    VDS_VT_SIMPLE

    The volume type is simple—it is composed of extents from exactly one disk.

    VDS_VT_SPAN

    The volume type is spanned—it is composed of extents from more than one disk.

    VDS_VT_STRIPE

    The volume type is striped, which is equivalent to RAID 0.

    VDS_VT_MIRROR

    The volume type is mirrored, which is equivalent to RAID 1.

    VDS_VT_PARITY

    The volume type is striped with parity, which accounts for RAID levels 3, 4, 5, and 6.

  • VDS_HEALTH – здоровье тома
# Загрузка сборки Microsoft Storage VDS Library  
[Reflection.Assembly]::LoadWithPartialName("Microsoft.Storage.Vds") | Out-Null 
$VdsServiceLoader = New-Object Microsoft.Storage.Vds.ServiceLoader 
$VdsService = $VdsServiceLoader.LoadService($null) 
$VdsService.WaitForServiceReady() 

# Получим коллекцию томов присутствующих в системе
$volumes = $VdsService.Providers  | foreach {$_.packs} | foreach {$_.volumes}

# Выведем свойства Буква диска,тип,статус,"здоровье" 
$volumes | Select DriveLetter,Type,Status,Health

Пример вывода:

Windows PowerShell

Copyright (C) 2012 Microsoft Corporation. All rights reserved.

PS >.\GetVolumeStatus.ps1

DriveLetter   Type Status  Health

————   —- ——  ——

          G  Mirror   Online  Healthy

          E  Span     Online  Healthy

          F  Stripe   Online  Healthy

          D  Simple   Online  Healthy 
             Simple   Online  Healthy

          C  Simple   Online  Healthy



 

3) Используя командлет Get-Volume из модуля Storage

Назначение:  Windows 8,  Windows Server 2012

В Windows 8,Windows Server 2012 появился новый WMI класс MSFT_Volume. Комадлет Get-Volume является обвязкой для этого класса. Есть один существенный минус – нет возможности определить тип тома.

Требования:

  • Minimum supported client — Windows 8 [desktop apps only]
  • Minimum supported server — Windows Server 2012 [desktop apps only]
  • Namespace — \\.\Root\Microsoft\Windows\Storage
  • MOF — Storagewmi.mof

Пример вывода:

Windows PowerShell

Copyright (C) 2012 Microsoft Corporation. All rights reserved.

PS > Get-Volume | Format-Table -AutoSize DriveLetter,HealthStatus

DriveLetter HealthStatus

———— ————

            Healthy

          C Healthy

          E Healthy

          F Healthy

            Healthy

          D Healthy



 

PS. ИМХО самое простое и эффективное средство – является DiskPart

Read Full Post »

  1. Создание ускорителей(Accelerators) в PowerShell V3
  2. Поддержка V3 в Windows Server Core 2008 R2
  3. Тип bool в WMI
  4. Не очень ясное поведение Uninstall-WindowsFeature
  5. Почему Get-ChildItem в PowerShell V3 стал еще медленнее
  6. Актуальные версии помощи
  7. Печать диапазона страниц в Word
  8. Изменение поведения Register-EngineEvent при использвании источника события Exiting
  9. Получить UUID компьютера
  10. Автоматическое сохранение в  PowerShell Ise
  11. Удаление System Restore Point
  12. Проверка пароля пользователя
  13. Получить путь родительского объекта symlink
  14. Одна из проблем с GUI скриптами при переходе c PowerShell V2 на PowerShell V3

1. Создание ускорителей(Accelerators) в PowerShell V3

В своей заметке “Новинки PowerShell V3 часть 3”,я описывал ,как получить и создать ускорители в PowerShell V3.С выходом RTM версии ситуация изменилась и теперь потребуется другая техника. в связи с изменением внутренней структуры.

Для получения списков ускорителей по умолчанию:

[psobject].assembly.gettype("System.Management.Automation.TypeAccelerators")::Get

Для создания и удаления:

$acceleratorsType = [type]::gettype("System.Management.Automation.TypeAccelerators")
$acceleratorsType::Add("accelerators", $acceleratorsType)
$ArcSegment  = [type]::gettype("System.Windows.Media.ArcSegment")

# Создание
[accelerators]::Add(‘ArcSegment’,$ArcSegment)

# Убедимся,что ускоритель был добавлен
[accelerators]::get

# Удалить

[accelerators]::Remove(‘ArcSegment’)

Более подробно можно посмотреть модуль — Reflection Module 4.0 by Joel Bennett.

2. Поддержка V3 в Windows Server Core 2008 R2

Для этого требуется установить Service Pack 1 и .Net 4.0.

3. Тип bool в WMI

При использовании типа bool с WMI/CIM,можно заменить отличие от .Net.

“Boolean” в CIM / WMI может содержать 3 значения: bool:null, bool:true, bool:false.

“Boolean” в .NET содержит 2 значения: bool:true и bool:false.

4. Не очень ясное поведение Uninstall-WindowsFeature

Попробуем удалить несуществующую службу.

PS > Uninstall-WindowsFeature IamNotExists –Verbose
VERBOSE: Uninstallation started…
Uninstall-WindowsFeature : ArgumentNotValid: The role, role service, or feature name is not valid: ‘IamNotExists’. The name was not found.
At line:1 char:1
+ Uninstall-WindowsFeature IamNotExists –Verbose
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (IamNotExists:String) [Uninstall-WindowsFeature], Exception
    + FullyQualifiedErrorId : NameDoesNotExist,Microsoft.Windows.ServerManager.Commands.RemoveWindowsFeatureCommand

Success Restart Needed Exit Code      Feature Result
——- ————— ———      —————
False   No             InvalidArgs    {}
VERBOSE: Uninstallation succeeded.

Как видим получаем ошибку,но и "VERBOSE: Uninstallation succeeded",по словам разработчиков это  не очень красивы вывод,что будет исправлено в следующем релизе.

5. Почему Get-ChildItem в PowerShell V3 стал еще медленнее

Командлет Get-ChildItem всегда не радовал своей скоростью работы,но в PowerShell V3,скорость работы может упасть в разы. Поэтому во многих случаях используют .Net или команду из cmd:

  • cmd /c dir  /s /b
  • Скажем в случае получения только подпапок,часто встречаем конструкцию вида :

Get-ChildItem folder –Recurse | Foreach {!$_.PsIsContainer} ,для ускорения,можно воспользоваться .Net GetDirectories,с одним большим НО не должно быть ссылок и при возникновении исключения  Access Denied  возникает Terminate Error ,и исполнение останавливается  ,в случае ссылок  произойдет зацикливание,а в случае с хорошо знакомой нам конструкцией такого не возникает.

(gi C:\Windows).GetDirectories("*.*","AllDirectories")

В связи с изменением устройства командлета Get-ChildItem, мы можем получить падение производительности,но с другой стороны стало удобнее.

# В PowerShell V2 рекурсивный поиск не производится,только в текущей папке
PS >  Get-ChildItem 1.csv -recurse
    Directory: C:\Test
Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        17.11.2012     11:50         70 1.csv

# В PowerShell V3 используется рекурсивный поиск
PS >  Get-ChildItem 1.csv -recurse
    Directory: C:\Test\1\2

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        17.11.2012     11:50         70 1.csv

    Directory: C:\Test
Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        17.11.2012     11:50         70 1.csv

Для получения аналогичного результата в PowerShell V2  — Get-ChildItem . 1.csv –recurse

В PowerShell V3 происходит проверка, является ли элемент каталогом. Если так, получаем список содержимого в этом каталоге. Если это файл, получаем  этот файл. Если файл не существует, тогда  рекурсивно перечислим его родительский каталог в поисках файлов или дочерний каталог. Это  алгоритм рекурсии  используется в cmd.exe команда dir.

В связи с этим,будет наблюдаться падение производительности,если корневая папка отсутствует,пока не будет произведен поиск до конца.

Зато подобный синтаксис позволяет(на примере dir c:\*.txt –rec и dir C:\file.txt –rec)

  • Производить поиск в текущей папке и всех ее подпапок на наличие текстовых файлов
  • Производить поиск в конкретной папке и всех ее дочерних подпапок на наличие текстовых файлов
  • Аналогично для file.txt

Более наглядный пример с потерей производительности,можно посмотреть на — Serious Get-ChildItem performance degradation in v3 by Kirk Munro. В примере потеря скорости наблюдается в 1300 раз,если корневая папка отсутствует,пока не будет произведен поиск до конца. Поэтому рекомендуется сначала протестировать существование объекта с помощью Test-Path и уже воспользоваться Get-ChildItem.

6. Актуальные версии помощи

В связи с новой системой обновления помощи с использованием командлета Update-Help в ведено понятие версий(подробнее о обновлении about_Updatable_Help). На сайте technet можно посмотреть актуальные версии помощи — Updatable Help Status.

Updated: November 12, 2012

ModuleName Locales Version
AppLocker en-US 3.1.0.0
ClusterAwareUpdating en-US 3.1.0.0
Microsoft.WSMan.Management en-US 3.2.0.0
NetSwitchTeam en-US 3.0.0.0

Для сравнения можно воспользоваться функцикй Get-HelpInfo — PS V3 by Oisin Grehan.

PS >  get-helpinfo | ft -AutoSize

ModuleName        UICulture Installed Available
----------        --------- --------- ---------
CimCmdlets        en-US     3.0.0.0   3.0.0.0
ISE               en-US     3.0.0.0   3.0.0.0
PSScheduledJob    en-US     3.1.0.0   3.1.0.0
PSWorkflow        en-US     3.2.0.0   3.2.0.0
PSWorkflowUtility en-US     3.0.0.0   3.0.0.0

Как происходит обновление с использованием Update-Help:

  1. Finds all of the modules that are installed on your computer (in the value of the PSModulePath environment variable) plus those in the current session.
  2. Finds the online location of Help files for each module.
  3. Checks to see if you have the latest Help files for the module on your computer. 
  4. Downloads the newest Help files if you don’t have them.
  5. Unpacks the Help files (they’re in CAB files).
  6. Verifies the XML and file types for security.
  7. Installs them in the language-specific subdirectory of the module directory.

Теперь у модулей,которые можно обновить с использованием Update-Help есть свойство – HelpInfoUri,указывающее на внешний источник,который содержит требуемые файлы.

PS >  dir $pshome\*HelpInfo.xml | ft name

Name
----
Microsoft.PowerShell.Core_00000000-0000-0000-0000-000000000000_HelpInfo.xml
Microsoft.PowerShell.Diagnostics_ca046f10-ca64-4740-8ff9-2565dba61a4f_HelpInfo.xml
Microsoft.PowerShell.Host_56d66100-99a0-4ffc-a12d-eee9a6718aef_HelpInfo.xml
Microsoft.PowerShell.Management_eefcb906-b326-4e99-9f54-8b4bb6ef3c6d_HelpInfo.xml
Microsoft.PowerShell.Security_a94c8c7e-9810-47c0-b8af-65089c13a35a_HelpInfo.xml
Microsoft.PowerShell.Utility_1da87e53-152b-403e-98dc-74d7b4d63d59_HelpInfo.xml
Microsoft.WSMan.Management_766204a6-330e-4263-a7ab-46c87afc366c_HelpInfo.xml

Update-Help ищет на локальном комьютере файлы *HelpInfo.xml в которых содержится версия.

PS > gc $PSHOME\modules\PSScheduledJob\*helpinfo.xml

<?xml version="1.0" encoding="utf-8"?>
<HelpInfo xmlns="http://schemas.microsoft.com/powershell/help/2010/05">
  <HelpContentURI>http://go.microsoft.com/fwlink/?LinkID=223911</HelpContentURI>
  <SupportedUICultures>
    <UICulture>
      <UICultureName>en-US</UICultureName>
      <UICultureVersion>3.1.0.0</UICultureVersion>
    </UICulture>
  </SupportedUICultures>
</HelpInfo>

PS C:\ > dir $PSHOME\modules\PSScheduledJob\*helpinfo.xml | 
    select-xml -Namespace $HelpInfoNamespace -XPath "//helpInfo:UICulture" | foreach {$_.Node}

UICultureName                                               UICultureVersion
-------------                                               ----------------
en-US                                                       3.1.0.0

Более подробно:

7. Печать диапазона страниц в Word

Требуется распечатать диапазон страниц с 5 по 10. Основную работу выполняет метод printout с описанием параметров.Главное учесть,что требуется указать все значения до требуемого параметра(т.е в данном примере нам требуется параметр Pages,поэтому и указываем Background,Append,Range,OutputFileName,From,To,Item,Copies)

$BackGround = 1
$Append     = 0
$Range      = 4
$Item       = 0
$Copies     = 1
#5-10
$Pages      = "5-9"

$Missing = [System.Reflection.Missing]::Value
$wd = New-Object -ComObject Word.Application
$doc = $wd.Documents.Open("C:\Files\Document.docx")
$doc.printout([ref]$BackGround, [ref]$Append, [ref]$Range, [ref]$Missing,[ref]$Missing,
    [ref]$Missing,[ref]$Item,[ref]$Copies,[ref]$Pages)
$doc.Quit()

8. Изменение поведения Register-EngineEvent при использвании источника события Exiting

В PowerShell V2 появился командлет Register-EngineEvent,который создает подписку на события, создаваемые обработчиком Windows PowerShell. При задании источника событий –SourceIdentifier  Exiting, что при завершении Windows PowerShell выполнялась команда. В PowerShell V2 событие Exiting возникало,только при использовании ключевого слова exit, при нажатии на кнопку Close(“X”) событие не возникало. Данное поведение было исправлено в PowerShell V3.

Register-EngineEvent -sourceIdentifier PowerShell.Exiting -action {Get-Process | add-content processLog.txt }

9. Получить UUID компьютера

(Get-WmiObject Win32_ComputerSystemProduct).UUID

10. Автоматическое сохранение в PowerShell Ise

AutoSaveMinuteInterval

Этот параметр является новым в PowerShell ISE v3 и  используется для задания интервала для автоматического сохранения скриптов. Эта функция не сохраняет автоматически файл, но он позволяет автоматического восстановливать несохраненные файлы в случае отказа ISE или непредвиденных ошибок. Это свойство принимает значение Int16, в диапазоне -32768 до 32767. Следующий параметр установить интервал авто сохранения в одну минуту.

$psISE.Options.AutoSaveMinuteInterval = 1

Установка отрицательное значения отключит функцию автосохранения ISE и способность восстановить несохраненные файлы в случае аварии.

Подобный функционал реализуется через Restart Manager API.

Про другие опции и улучшения — PowerShell v3 ISE and ISE scripting model changes & improvements

11. Удаление System Restore Point

В PowerShell V2 появился набор командлетов для управления System Restore Point, но нет возможности удалять точки. Для этого потребуется WinApi функция SRRemoveRestorePoint.

Пример скрипта удаления точек старше 14 дней:

$point = Add-Type  -memberDefinition @"
                [DllImport("Srclient.dll")]
                public static extern int SRRemoveRestorePoint(int index);
"@ -Name Win32 -NameSpace System -PassThru

$w = (Get-Date).AddDays(-14)
$mdtc = [Management.ManagementDateTimeConverter] -as [type]

Get-ComputerRestorePoint | Where {$mdtc::ToDateTime($_.CreationTime) -lt $w} | 
    Foreach {$point::SRRemoveRestorePoint($_.SequenceNumber)

12. Проверка пароля пользователя

Проверка стандартного пароля у пользователя в Active Directory. В .Net 2.0 можем использовать:

# Запрос учетных данных
$cred = Get-Credential 
$username = $cred.username
$password = $cred.GetNetworkCredential().password

# Пробуем произвести подключение
$Domain = "LDAP://DC=contoso,DC=com"
$ds = New-Object System.DirectoryServices.DirectoryEntry($Domain,$UserName,$Password)

if ($ds.name){
    "Success"
}
else {
    "Failed"
}

В  .Net 3.5 появился метод PrincipalContext.ValidateCredentials:

$cred = Get-Credential
Add-Type -assemblyname system.DirectoryServices.accountmanagement
$DS = New-Object System.DirectoryServices.AccountManagement.PrincipalContext("Domain") 
$DS.ValidateCredentials($cred.UserName, $cred.GetNetworkCredential().password)

13. Получить путь родительского объекта symlink

Самый простой использовать команду dir в cmd.exe.

PS >  cmd /c dir D:\ /AL

Volume in drive D is Новый том

Volume Serial Number is 1AAB-1B76

Directory of D:\

18.11.2012  19:08    <SYMLINKD>     2 [D:\1]

Метод является не очень удобным,для решения воспользуемся функцией GetFinalPathNameByHandle ,код на C# заимствован у

http://chrisbensen.blogspot.ru/2010/06/getfinalpathnamebyhandle.html :

param (
    $path
)

Add-Type -MemberDefinition @"
private const int FILE_SHARE_READ = 1;
private const int FILE_SHARE_WRITE = 2;

private const int CREATION_DISPOSITION_OPEN_EXISTING = 3;
private const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;

[DllImport("kernel32.dll", EntryPoint = "GetFinalPathNameByHandleW", 
    CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int GetFinalPathNameByHandle(IntPtr handle, 
    [In, Out] StringBuilder path, int bufLen, int flags);

[DllImport("kernel32.dll", EntryPoint = "CreateFileW", 
    CharSet = CharSet.Unicode, SetLastError = true)]
public static extern SafeFileHandle CreateFile(string lpFileName, 
    int dwDesiredAccess, int dwShareMode,
IntPtr SecurityAttributes, int dwCreationDisposition, 
    int dwFlagsAndAttributes, IntPtr hTemplateFile);

public static string GetSymbolicLinkTarget(System.IO.DirectoryInfo symlink)
{
    SafeFileHandle directoryHandle = CreateFile(symlink.FullName, 
        0, 2, System.IntPtr.Zero, 
        CREATION_DISPOSITION_OPEN_EXISTING, 
        FILE_FLAG_BACKUP_SEMANTICS, System.IntPtr.Zero);
        
    if(directoryHandle.IsInvalid)
    throw new Win32Exception(Marshal.GetLastWin32Error());

    StringBuilder path = new StringBuilder(512);
    int size = GetFinalPathNameByHandle(directoryHandle.DangerousGetHandle(),
        path, path.Capacity, 0);
    if (size<0)
    throw new Win32Exception(Marshal.GetLastWin32Error());
    if (path[0] == '\\' && path[1] == '\\' && path[2] == '?' && path[3] == '\\')
    return path.ToString().Substring(4);
    else
    return path.ToString();
}
"@ -Name Win32 -NameSpace System -UsingNamespace System.Text,`
    Microsoft.Win32.SafeHandles,System.ComponentModel

$dir = Get-Item $path 
[System.Win32]::GetSymbolicLinkTarget($dir)

14. Одна из проблем с GUI скриптами при переходе c PowerShell V2 на PowerShell V3

Для примера возьмем скрипт  — http://technet.microsoft.com/en-us/library/ff730941.aspx . Данный скрипт  работает в V2,но V3 нет. Для этого явно надо задать область(script,global):

$OKButton.Add_Click({$global:x=$objTextBox.Text;$objForm.Close()})

PS. Является багом, т.к. нарушена обратная совместимость с предыдущими версиями.Подобные проблемы можно наблюдать и в использовании делегатов.

PS >  $i=0;[Regex]::Replace(‘A+B+C+D’,’\+’,{($global:i++)})

A0B1C2D

PS  >  $i=0;[Regex]::Replace(‘A+B+C+D’,’\+’,{($i++)})

A0B0C0D

 

Read Full Post »

Некоторые атрибуты у объектов могут содержать достаточно большое количество значений. В этой заметке будем рассматривать на примере атрибута member с использованием средств .Net. Предположим,что у нас есть группа с количество членов 6000. Используя акселератор [ADSI],попробуем получить количество членов в группе:

PS >$gr= [ADSI]«LDAP://CN=Big,OU=Groups,OU=Contoso,DC=contoso,DC=com«
PS >$gr.member.count
1500

Мы получили значение 1500,что не соответствует  6000. Теперь разберемся почему так и как можно обойти это ограничение по умолчанию.

1) Политики LDAP в Active Directory

Чтобы убедиться, что контроллеры домена могут поддерживать заданный уровень  обслуживания , вам нужно указать, эксплуатационных ограничений для ряда Lightweight Directory Access Protocol (LDAP) операций. Эти ограничения предотвращения в конкретных операций отрицательно влияющих на производительность сервера, а также сделать сервер более устойчивым к DDoS-атакам.

Применение LDAP политики осуществляется с помощью объектов класса queryPolicy. Query Policy объекты могут быть созданы в контейнере Query Policy, который является дочерним Directory Service контейнер в  контексте именования Сonfiguration. Например:

cn=Query-Policies, cn=Directory Service, cn=Windows NT, cn=Services,CN=Configuration,DC=forest root

Контроллер домена использует следующие три механизма для применения LDAP политики:

  • Контроллер домена может ссылаться на конкретные LDAP политики. В nTDSASettings объект включает в себя дополнительный атрибут queryPolicyObject, которая содержит различающееся имя(distinguished name,DN)  Query Policy.
  • В отсутствие конкретного query policy, применяемых к контроллеру домена, контроллер домена применяется Query Policy, который был назначен для контроллера домена сайта. В ntDSSiteSettings объект включает в себя дополнительный атрибут queryPolicyObject, которая содержит различающееся имя Запроса Политики.
  • В отсутствие конкретного контроллера домена или сайта Query Policy, контроллер домена использует запросов по умолчанию политики имени по Default-Query Policy.

Для изменения и просмотра Query Policy, мы может воспользоваться несколькими инструментами:

  • ntdsutil
  • dsmgmt
  • оснастка ADSI Editor
  • ldp

Более подробно про параметры Query Policy, можно прочитать:

Нас будет интересовать параметр MaxValRange:

MaxValRange — данное значение определяет количество возвращаемых для атрибута объекта значений, вне зависимости от количества атрибутов объекта или количество объектов в результате поиска. В Windows 2000 для этого параметра установлено значение 1 000. Если количество значений атрибута превышает указанное значение MaxValRange, для получения значений, превышающих значение MaxValRange, необходимо использовать параметр дипазона значений в LDAP. MaxValueRange определяет количество значений, возвращаемых для одного атрибута одного объекта.

ОС контроллера домена Значение по умолчанию
Windows Server 2000* 1000
Windows Server 2003 1500
Windows Server 2003 R2 1500
Windows Server 2008 1500
Windows Server 2008 R2 1500
Windows Server 2012 1500

* — Windows 2000 DCs не поддерживают данную политику и всегда используют значение 1000.

Значение параметра MaxValRange для 2008,2008 R2,2012 жестко ограниченно в 5000.

Это значение можно переопределить,подробнее:

Для переопределения верхнего лимита введенного в Windows Server 2008/R2 и восстановления старого стиля (нет верхнего предела ограничения для LDAP Query Policy в Windows Server 2003), требуется изменить атрибут dSHeuristic в Active Directory. Выполните следующие шаги:

  1. Start ADSI Edit. To do this, open a command prompt in the Support Tools folder, type ADSIEDIT.MSC, press Enter
  2. Right-click CN=Directory Service in the following location, and then click Properties: CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration,DC=forest root
  3. Click the Attribute Editor tab, and then locate dSHeuristic in the Attributes list.
    NoteBy default, the value of this attribute is not set.
  4. Click dSHeuristic, and then click Edit.
  5. Type 000000000100000001 in the Value box, and then click OK. See Notebelow.
  6. Restart the Active Directory Domain Service (NTDS) or the domain controller.

Note If a value has already been set for this attribute, incorporate the existing settings into the new value. When you do this, note the following:

  • The tenth character from the left must be 1. Twentieth bit must be 2, and so on.
  • The eighteenth character from the left must be 1.
  • None of the other characters of the existing value should be changed. For instance, if the existing value is 0000002 then the new value should be 000000200100000001

Будьте осторожны с изменением значения MaxValRange, это может вызвать проблемы:

Посмотрим и изменим значение MaxValRange:

C:\Windows\system32\ntdsutil.exe: ldap policies
ldap policy: connect
server connections: connect to server srv-dc
Binding to srv-dc ...
Connected to srv-dc using credentials of locally logged on user.
server connections: q
ldap policy: show values

Policy                          Current(New)

MaxPoolThreads                  4
MaxDatagramRecv                 4096
MaxReceiveBuffer                        10485760
InitRecvTimeout                 120
MaxConnections                  5000
MaxConnIdleTime                 900
MaxPageSize                     1000
MaxBatchReturnMessages                  0
MaxQueryDuration                        120
MaxTempTableSize                        10000
MaxResultSetSize                        262144
MinResultSets                   0
MaxResultSetsPerConn                    0
MaxNotificationPerConn                  5
MaxValRange                     1500

ldap policy: setMaxValRangeto5000
ldap policy: Commit Changes
ldap policy: q
C:\Windows\system32\ntdsutil.exe: q

Теперь опять выполним код:

PS > $gr= [ADSI]"LDAP://CN=Big,OU=Groups,OU=Contoso,DC=contoso,DC=com"PS > $gr.member.count
5000
Данный способ подходит в случае если у вас точно нет атрибутов с количеством значений 
больше 5000 или используйте способ выше,не поддерживаемый Microsoft.
Поэтому воспользуемся механизмом "range retrieval".

2) Поиск с использованием атрибута Range 

При получении значений многозначного атрибута, Active Directory ограничивает количество значений, которые могут  быть извлечены из одного атрибута в одном поисковом запросе. Максимальное количество значений, которые будут возвращены в одном запросе определяется параметром MaxValRange. Для разрешения всех значений многозначного атрибута, используется механихм  «range retrieval». Этот механизм позволяет клиенту указанное подмножество значений, которые будут получены в поисковом запросе. При выполнении нескольких поисковых запросов, каждый запрос возвращает различное подмножество, полный набор значений атрибута может быть извлечен.

Спецификатор Range для атрибута запроса требуют следующую форму:

range=<low range>-<high range>

<low range> — начинается с нуля и является первый элементом атрибутом

<high range> — является отсчитываемый от нуля индекс извлекаемого последнего значения атрибута.Подстановочный знак (*) может использоваться для <high range> , для указания всех оставшихся записей.

В следующей таблице перечислены примеры диапазон спецификаторы.

Пример Значение
range=0-* Возвращает все значения атрибута. Ограничен параметром MaxValRange(по умолчанию 1500).
range=0-500 Возвращает с 1 по 501 значение атрибута.
range=2-3 Возвращает 3 и 4 значение атрибута.
range=501-* Возвращает с 502 и все оставшиеся значения атрибута.Ограничен параметром MaxValRange(по умолчанию 1500).

Когда сервер Active Directory возвращает значения атрибута member в результате запроса поиска каталога, его поведение  зависит от общего количества значений атрибута для этого объекта, если превышает максимальный предел  значения. Например если список рассылки на сервере Windows 2000 Server содержит 1000 или меньше значения членов, запрос поиска возвращает все значения в один вызов. Однако, если список содержит значения членов 2497, первый вызов функции запросов поиска возвращает атрибут member без значений и дополнительного member; range = 0-999 атрибут, содержащий первые 1000 значений. Для получения следующей группы значений , следует повторить запрос поиска с помощью спецификатора range, который начинается с последнего номера значения, возвращаемого из предыдущего запроса. В этом примере функция запроса поиска будет запрашивать значения атрибута member;range = 1000-* , вернет перую 1000 значений атрибута  и member; range = 1000-1999 вернет следующие 1000 значений атрибута. Этот процесс повторяется до тех пор, пока не будет получено последнее значени. Конец диапазона последней группы, полученных с сервера будет указываться (звездочка) в имени возвращенного атрибута. Для 2497: member;range=0-998 – вернет первую 1000 значений member;range=999-1998 – вернет вторую 1000 значений member;range=1998-* – вернет все оставшиеся значения Для 0-999: member;range=0-* Перейдем к коду на PowerShell для получения значений атрибута member. Для этого воспользуемся классом DirectorySearcher,как одним из вариантов решения, так же можно посмотреть решение с использованием ADO (скрипт предоставил MVP Directory Service Richard Mueller — EnumLargeGroup.ps1).

#Нижний предел

$LowRange=0
#Шаг
#To enable an application to work correctly with all Windows servers, 
#a maximum number of 1000 should be used.

$RangeStep=999

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

while ($true)
{
    #Врехний предел(0-998 и т.д.)

$HighRange=$LowRange+$RangeStep-1

#Атрибут

$Properties="member;range=$LowRange-$HighRange"

#Конструктор для DirectorySearcher, AdsPath - Dn,$Filter -может быть любой                                                   $Searcher=New-ObjectDirectoryServices.DirectorySearcher(
        $AdsPath, $Filter, $Properties, "Base"
    )

    #Получаем значение атрибута membertry {
        $Group=$Searcher.FindOne().Properties

        $Attribute= ($Group.GetEnumerator() | 
            Where {$_.Name -match"member"}).Name

        $Group[$Attribute]
    }

    catch {
         break
    }

    $LowRange+=$RangeStep
}

Я написал функцию для более удобного использования.

Function Get-GroupMember
{
    [CmdletBinding()]
    Param (
	    [Parameter(Mandatory=$true,ValueFromPipeline=$true,
			ValueFromPipelineByPropertyName=$true)]
	    [Alias("DN","DistinguishedName")]
	    $Name,
	    [uint32]$HighRange,
	    [uint32]$LowRange = 0,
	    [switch]$CountMembers,
	    [string]$Server
    )

    begin {
        $RangeStep = 999
		$Filter = "(&(objectClass=Group)(objectCategory=Group))"
    }

	    process {
		$LowRange = [int]$PSBoundParameters["LowRange"]

		if($LowRange -gt 0) {$LowRange -= 1}
		if($HighRange -gt 0) {$HighRange -= 1}

		$Members = @()

		if($Server) {
			$AdsPath = "LDAP://$Server/$Name"
		}
		else {
			$AdsPath = "LDAP://$Name"
		}

		$IsExists = $false

		if([DirectoryServices.DirectoryEntry]::Exists($AdsPath)) {
			if($HighRange) {
			    while ($LowRange -lt $HighRange)
			    {
			        $MiddleRange = $LowRange + $RangeStep -1
					if($MiddleRange -gt $HighRange) {
						 $Properties = "member;range=$LowRange-$HighRange"
					}
					else {
					 	$Properties = "member;range=$LowRange-$MiddleRange"
					}

			        $Searcher = New-Object DirectoryServices.DirectorySearcher(
						$AdsPath, $Filter, $Properties, "Base"
					)

			        try {
				        $Group = $Searcher.FindOne().Properties

						$Attribute = ($Group.GetEnumerator() |
								Where {$_.Name -match "member"}).Name

						Write-Verbose "$Name - $Attribute"
				        $Members += $Group[$Attribute]
			        }

					catch {
			            break
			        }

			        $LowRange  += $RangeStep
			    }
			}

			else {
				while ($true)
			    {
			        $HighRange = $LowRange + $RangeStep -1
			        $Properties = "member;range=$LowRange-$HighRange"

			        $Searcher = New-Object DirectoryServices.DirectorySearcher(
						$AdsPath, $Filter, $Properties, "Base"
					)

			        try {
				        $Group = $Searcher.FindOne().Properties

						$Attribute = ($Group.GetEnumerator() |
								Where {$_.Name -match "member"}).Name

						Write-Verbose "$Name - $Attribute"
				        $Members += $Group[$Attribute]
			        }

					catch {
			            break
			        }

			        $LowRange  += $RangeStep
			    }
			}

			$IsExists = $true
		}

		else {
			Write-Host "The path $Name is invalid" -ForegroundColor Yellow
		}

		if ($IsExists)
		{
			if($CountMembers){
				$Members.Count
			}

			else {
				$Members
			}
		}
    }
}

Примеры работы функции:

1) Получить количество членов в группе


PS > Get-GroupMember "CN=Big,OU=Groups,OU=Contoso,DC=contoso,DC=com" -CountMembers
6000
PS > "CN=Big,OU=Groups,OU=Contoso,DC=contoso,DC=com" | Get-GroupMember -CountMembers
6000

PS > Get-GroupMember "CN=Big,OU=Groups,OU=Contoso,DC=contoso,DC=com" -CountMembers -Verbose
VERBOSE: CN=Big,OU=Groups,OU=Contoso,DC=contoso,DC=com - member;range=0-998
VERBOSE: CN=Big,OU=Groups,OU=Contoso,DC=contoso,DC=com - member;range=999-1997
VERBOSE: CN=Big,OU=Groups,OU=Contoso,DC=contoso,DC=com - member;range=1998-2996
VERBOSE: CN=Big,OU=Groups,OU=Contoso,DC=contoso,DC=com - member;range=2997-3995
VERBOSE: CN=Big,OU=Groups,OU=Contoso,DC=contoso,DC=com - member;range=3996-4994
VERBOSE: CN=Big,OU=Groups,OU=Contoso,DC=contoso,DC=com - member;range=4995-5993
VERBOSE: CN=Big,OU=Groups,OU=Contoso,DC=contoso,DC=com - member;range=5994-*
6000

2)Получить первые 500 значений


PS > Get-GroupMember "CN=Big,OU=Groups,OU=Contoso,DC=contoso,DC=com" -HighRange 500 -CountMembers
500
PS > Get-GroupMember "CN=Big,OU=Groups,OU=Contoso,DC=contoso,DC=com" -HighRange 500 -CountMembers
-Verbose
VERBOSE: CN=Big,OU=Groups,OU=Contoso,DC=contoso,DC=com - member;range=0-499
500

3)Получить значения с 1000 по 3000


PS > Get-GroupMember "CN=Big,OU=Groups,OU=Contoso,DC=contoso,DC=com" -LowRange 1000 -HighRange 3000 -CountMembers -Verbose
VERBOSE: CN=Big,OU=Groups,OU=Contoso,DC=contoso,DC=com - member;range=999-1997
VERBOSE: CN=Big,OU=Groups,OU=Contoso,DC=contoso,DC=com - member;range=1998-2996
VERBOSE: CN=Big,OU=Groups,OU=Contoso,DC=contoso,DC=com - member;range=2997-2999
2001

PS. Оба метода являются громоздкими,но более производительными нежели командлеты из модуля для Active Directory. Командлеты Get-ADGroupMember,Get-ADObject не страдают ограничением на количество возвращаемых значений. Поэтому советую использовать данные командлеты.

Read Full Post »

Немного переименовал,может быть, не очень информативную название темы “Q&A”. В данной порции примеров приведу в основном о PowerShell V3,WMI,Active Directory и их применение.

  1. Как определить время старта и окончания Job в PowerShell V3?
  2. Как отправить почтовое сообщение на нестандартный порт в PowerShell V3?
  3. Какое новое свойство добавлено ко всем WMI объектам в PowerShell V3?
  4. Какие новые параметры появились у командлета Get-Command в PowerShell V3?
  5. Какую новую область видимости добавили в PowerShell V3?
  6. Как использовать удаленные Com объекты ?
  7. Почему Invoke-WmiMethod и [wmiclass] ведут себя по разному?
  8. Какой метод .Net используется в Set-WMIInstance для создания экземпляров?
  9. Как создать отсортированный hashtable?
  10. Как найти distinguishedname объекта по objectGuid?

1. Как определить время старта и окончания Job в PowerShell V3?

В версии PowerShell V3 добавились требуемые нам свойства,что облегчает определить продолжительность выполнения Job.

PS >$job = Start-Job {"Hello"}
PS >$job |Format-List *

PSBeginTime   :3/28/2012 10:12:29 AM
PSEndTime     :3/28/2012 10:12:31 AM

2. Как отправить почтовое сообщение на нестандартный порт в PowerShell V3?

В версии PowerShell V3 разработчики добавили параметр “Port“ для комадлета Send-MailMessage.

Пользователи,которые работают с Gmail указывают порт 587,т.к в PowerShell V2

не было параметра “Port“, требовалось использовать .Net функционал.

Send-MailMessage -SmtpServer smtp.test.com -From test@test.com -To user@user.com  `
    -Subject 'Test 587 Port' -Port 587 -Credential test@test.com -Body "Works" –UseSsl

Для версии PowerShell V2:

$EmailFrom = "test@test.com"
$EmailTo = "user@user.com"
$Subject = "Test 587 Port"
$SMTPServer = "smtp.test.com"
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587)
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential("username", "password");
$SMTPClient.Send($EmailFrom, $EmailTo, $Subject, $Body)

3. Какое новое свойство добавлено ко всем WMI объектам в PowerShell V3?

AliasProperty — свойство, определяющее новое имя существующего свойства

Здесь мы видим,что добавили новое свойство PSComputerName типа AliasProperty,

которое ссылается на значение __SERVER. Что удобно будет использовать в Advanced Functions.

PS > [wmi]"" | Get-Member

   TypeName: System.Management.ManagementObject#\

Name                MemberType    Definition
----                ----------    ----------
PSComputerName      AliasProperty PSComputerName = __SERVER

4. Какие новые параметры появились у командлета Get-Command в PowerShell V3?

В версии PowerShell V3 к командлету Get-Command добавились 4 новые параметра.

-CommandCapability<FlagsExpression<CommandCapability>>

Получает только команды с указанными возможностями. Допустимыми значениями являются Cmdlet, Script, Workflow, CIM, ScriptFile, Application, and Unknown.

Свойство  Capability команды указывает, что команда может сделать и как это можно использовать.Например, простые функции обладают возможностями Script, принимая во внимание, что расширенные функции имеют возможности Script и Cmdlet.

-ListImported

Получает только команды в текущей сессии.

Начиная с Windows PowerShell 3.0, по умолчанию, Get-Command получает все доступные команды, не ограничиваясь, только командами в текущей сессии.

-ParameterName<String[]>

Получает сведения только о командах  в сессии с указанным именем параметра.  Wildcard поддерживаются.

-ParameterType<PSTypeName[]>

Получает сведения только о командах в сессии с указанным типом параметра.Введите полное имя или часть имени типа  параметра. Wildcard поддерживаются.

С параметром CommandCapability мне не очень пока ясно. В основном,за исключением Cmdlet(но не Cmdlet,Script) и Application, можно посмотреть исходный код, свойство ScriptBlock.

Значением Unknown, тоже не очевидно.

PS > Get-Command -CommandCapability Unknown

Capability      Name                                               ModuleName
----------      ----                                               ----------
Unknown         Add-ProvisionedAppxPackage                         Dism
Unknown         Apply-WindowsUnattend                              Dism
Unknown         Begin-WebCommitDelay                               WebAdministration
Unknown         End-WebCommitDelay                                 WebAdministration
Unknown         gcai ->                                            CimCmdlets
Unknown         gcim ->                                            CimCmdlets

В основном это командлеты и альясы, но причем, как-то выборочно. Эти модули по умолчанию не импортированы  в текущую сессию. После импортирования они уже будут корректно определяться. Так же видим, gcai –> (в данном случае это альяс из модуля  CimCmdlets, но на что он ссылается, будет доступно только после импортирования модуля).

PS > Get-Command Add-ProvisionedAppxPackage

Capability      Name                                               ModuleName
----------      ----                                               ----------
Cmdlet          Add-ProvisionedAppxPackage                         Dism

PS > Get-Command gcai

Capability      Name                                               ModuleName
----------      ----                                               ----------
Cmdlet          gcai -> Get-CimAssociatedInstance

Примеры:

#Получить все команды у которых есть параметр Credential
Get-Command -ParameterName Credential

#Получить все команды у которых есть параметр Name типа String
Get-Command -ParameterName Name -ParameterType String

#Получить все команды в текущей сессии
Get-Command -ParameterName Credential

Для версии PowerShell V2 ,для поиска параметров с определенным именем, можно воспользоваться Get-Help или Get-Command.

Get-Help * -Parameter Name
Get-Command * -CommandType Cmdlet | Where {$_.Parameters["Name"]}

5. Какую новую область видимости добавили в PowerShell V3?

Using:

Указывает на локальную переменную. Используйте область видимости Using для удаленных команд, чтобы указать, что переменная находится в локальной сессии. По умолчанию, переменные в удаленных командах считаются определенными в удаленной сессии.

В версии PowerShell V2 для передачи параметра в scriptblock (можно  локально ), приходилось использовать ключевое слово param.

# Получить все процессы s* на удаленном компьютере
Invoke-Command -ComputerName localhost -ScriptBlock { param($name) Get-Process $name } -Arg "s*"

Особенно часто ошибка возникает,когда функцию определяют в локальной сессии, а хотят использовать в удаленной.

#Ошибка
Function Get-ProcName
{
    Get-Process | Foreach {$_.Name}
}

Invoke-Command -ComputerName localhost -ScriptBlock { Get-ProcName}

PS >  Invoke-Command -ComputerName localhost -ScriptBlock { Get-ProcName}
The term 'Get-ProcName' is not recognized as the name of a cmdlet, function, script file, or operable program. Check
the spelling of the name, or if a path was included, verify that the path is correct and try again.

Т.к Function это тот же scriptblock, для избежание ошибки, можно использовать множество вариантов, приведу один из:

$sb = {
    Function Get-ProcName
    {
        Get-Process | Foreach {$_.Name}
    }
    Get-ProcName
}

Invoke-Command -ComputerName localhost -ScriptBlock $sb

В версии PowerShell V3 для этого воспользуемся Using .

# Получить все процессы s* на удаленном компьютере
$name = “s*”
Invoke-Command -ComputerName localhost -ScriptBlock {Get-Process $using:name}

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

Invoke-Command -ComputerName localhost -ScriptBlock {Invoke-Expression ${using:function:Get-ProcName}}

6. Как использовать удаленные Com объекты ?

Для создания удаленного экземпляра Com объекта воспользуемся .Net функционалом.

#ProgId например hnetcfg.fwpolicy2
[System.Activator]::CreateInstance([type]::GetTypeFromProgID("ProgId", "ComputerName"))

Зарегистрированные ProgId в системе,можно найти с помощью:

Get-WmiObject Win32_ClassicCOMClassSetting | Where {$_.ProgId} | Format-Table ProgId

7. Почему Invoke-WmiMethod и [wmiclass] ведут себя по разному?

Пример объяснения приведу на WMI классе MicrosoftDNS_ResourceRecord и методе CreateInstanceFromTextRepresentation.

Синтаксис метода CreateInstanceFromTextRepresentation:

void CreateInstanceFromTextRepresentation(

[in] string DnsServerName,

[in] string ContainerName,

[in] string TextRepresentation,

[out, ref] MicrosoftDNS_ResourceRecord &RR

);

Теперь создадим DNS запись типа TXT используя [wmiclass]:

#Имя Dns сервера
$DnsServerName = "dc1.contoso.com"

#Имя зоны , где создать запись
$ContainerName = "contoso.com"

#Текстовое представление записи, если значение содержит пробелы, заключить в двойные кавычки
$TextRepresentation = "testtxt.contoso.com IN TXT ""This is a text"""

#Создадим wmi объект
$wmi = [wmiclass]"root\MicrosoftDNS:MicrosoftDNS_ResourceRecord"

#Выполним метод CreateInstanceFromTextRepresentation
$wmi.CreateInstanceFromTextRepresentation(
    $DnsServerName,
    $ContainerName,
    $TextRepresentation
    )

Для определения,какие параметры принимает метод CreateInstanceFromTextRepresentation, можно воспользоваться:

PS > $wmi.CreateInstanceFromTextRepresentation.OverloadDefinitions
System.Management.ManagementBaseObject CreateInstanceFromTextRepresentation(
    System.String DnsServerName,
    System.String ContainerName, System.String TextRepresentation
)

С [wmiclass] проблем не возникает при создании записи. Попробуем использовать Invoke-WmiMethod и получаем ошибку.

PS > Invoke-WmiMethod -Namespace "root\MicrosoftDNS"  -Class MicrosoftDNS_ResourceRecord `
>> -Name  CreateInstanceFromTextRepresentation -argumentlist $DNSServer,$ContainerName,$TextRepresentation

Invoke-WmiMethod : Invalid parameter
At line:1 char:1
+ Invoke-WmiMethod -Namespace "root\MicrosoftDNS"  -Class MicrosoftDNS_ResourceRec ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Invoke-WmiMethod], ManagementException
    + FullyQualifiedErrorId : InvokeWMIManagementException,Microsoft.PowerShell.Commands.InvokeWmiMethod

Данная ошибка не очень информативная, синтаксис с виду правильный, но не работает. Проблема в том,что Invoke-WmiMethod  используем свой порядок входных параметров и внутри использует .Net метод GetMethodParameters.

PS > $wmi.GetMethodParameters("CreateInstanceFromTextRepresentation")

__GENUS            : 2
__CLASS            : __PARAMETERS
__SUPERCLASS       :
__DYNASTY          : __PARAMETERS
__RELPATH          :
__PROPERTY_COUNT   : 3
__DERIVATION       : {}
__SERVER           :
__NAMESPACE        :
__PATH             :
ContainerName      :
DnsServerName      :
TextRepresentation :

Здесь хорошо видно,что порядок входных параметров должен быть – ContainerName,DnsServerName,TextRepresentation. Что отличается от синтаксиса,который в msdn или при CreateInstanceFromTextRepresentation.OverloadDefinitions .

Выполним Invoke-WmiMethod  с корректным порядком входных параметров:

PS > invoke-wmimethod -Namespace "root\MicrosoftDNS"  -Class MicrosoftDNS_ResourceRecord `
>>     -Name  CreateInstanceFromTextRepresentation -argumentlist $ContainerName,$DNSServer,$TextRepresentation

__GENUS          : 2
__CLASS          : __PARAMETERS
__SUPERCLASS     :
__DYNASTY        : __PARAMETERS
__RELPATH        :
__PROPERTY_COUNT : 1
__DERIVATION     : {}
__SERVER         :
__NAMESPACE      :
__PATH           :
RR               : MicrosoftDNS_TXTType.RecordData="\"This is a text\"",RecordClass=1,DnsServerName="DC1.contoso.
                   com",ContainerName="contoso.com",DomainName="contoso.com",OwnerName="testtxt.contoso.com"

Командлет Invoke-WmiMethod,  отработал, как полагается.

8. Какой метод .Net используется в Set-WMIInstance для создания экземпляров?

Данный командлет использует .Net метод  CreateInstance — ((ManagementClass) instance.InputObject).CreateInstance();. Не все классы поддерживают создание экземпляра, хотя с виду и создают его.

Приведу пример с WMI классом MicrosoftDNS_ResourceRecord

# Получим ошибку,т.к данный класс не поддерживает создания записей таким способом, используйте метод CreateInstanceFromTextRepresentation
Set-WMIInstance -Namespace root\MicrosoftDNS -class MicrosoftDNS_ResourceRecord -argument @{
    DnsServerName = $DNSServer;
    ContainerName = $Domain;
    DomainName = $Domain;
    OwnerName = $CN;
    RecordClass=1;
    RecordData = $TXT;
    TTL = 3600;
    TextRepresentation = $TextRepresentation;
    Timestamp = 0
}

PS > $wmi.CreateInstance()

__GENUS            : 2
__CLASS            : MicrosoftDNS_ResourceRecord
__SUPERCLASS       : CIM_LogicalElement
__DYNASTY          : CIM_ManagedSystemElement
__RELPATH          :
__PROPERTY_COUNT   : 14
__DERIVATION       : {CIM_LogicalElement, CIM_ManagedSystemElement}
__SERVER           :
__NAMESPACE        :
__PATH             :
Caption            :
ContainerName      :
Description        :
DnsServerName      :
DomainName         :
InstallDate        :
Name               :
OwnerName          :
RecordClass        : 1
RecordData         :
Status             :
TextRepresentation :
Timestamp          :
TTL                :
PSComputerName     :

С классом MicrosoftDNS_TXTType , таких проблем не возникает :

Set-WMIInstance -Namespace root\MicrosoftDNS -class MicrosoftDNS_TXTType -argument @{
    DnsServerName = $DNSServer;
    ContainerName = $Domain;
    DomainName = $Domain;
    OwnerName = $CN;
    RecordClass=1;
    RecordData = $TXT;
    TTL = 3600;
}

9. Как создать отсортированный hashtable?

В версии PowerShell V3 мы можем воспользоваться атрибутом [ordered].

[ordered]@{a=1;b=2;c=3}

Для версии PowerShell V2, можно воспользоваться:

1) $hast.GetEnumerator() | Sort name
2) [collections.sortedlist]$hash
3)  $OrderedList = New-Object System.Collections.Specialized.OrderedDictionary
      $OrderedList.Add("a","1")
      $OrderedList.Add("b","2")
      $OrderedList.Add("c","3")

10. Как найти distinguishedname объекта по objectGuid?

Практический пример применения это использование для определения DN из событий журнала Security.Возьмем для примера событие под номером 566(4662).

PS > Get-EventLog -LogName security -InstanceId 4662 | select -First 1 -ExpandProperty message
An operation was performed on an object.

Object:
        Object Server:          DS
        Object Type:            %{19195a5b-6da0-11d0-afd3-00c04fd930c9}
        Object Name:            %{7ac689ea-20ed-48eb-93c7-c97d5ae20fd4}
        Handle ID:              0x0

Вместо DN здесь представлен objectGuid, в оснастке EventLog отображается DN объекта.Для поиска Dn по ObjectGuid:

$guid = "7ac689ea-20ed-48eb-93c7-c97d5ae20fd4"
$bguid = -join (([GUID]$guid).ToByteArray() |% {"\{0:x}" -f $_})
$dn = ([adsisearcher]"(objectguid=$bguid)").FindOne().Properties.distinguishedname

Более наглядный пример, найдем все события с EventId 4662 и добавим новое свойство DNName  к объекту:


Get-WinEvent -FilterHashtable @{LogName="Security";Id=4662} | Foreach {
    $xevent = [xml]$_.ToXml()
    [Guid]$guid = $xevent.Event.EventData.Data | Where {$_.name -eq "ObjectName"} | Foreach {$_."#text" -replace "\W"}
    $bguid = -join ($guid.ToByteArray() |% {"\{0:x}" -f $_})
    [string]$dn = ([adsisearcher]"(objectguid=$bguid)").FindOne().Properties.distinguishedname
    $_ | Add-Member -Name DNName -Value $dn -MemberType NoteProperty -PassThru
}

 

 

Read Full Post »

Older Posts »