В этой статье рассмотрим почему мы все любим Powershell на паре рабочих примеров. Все это будет полезно при инвентаризации, поиске конкретного компьютера и прочих делах. Powershell оборудован удобными и запоминающимися командлетами, автодополнение всего и интуитивные названия делают его наверное самым простым в освоении языком.

В этом руководстве рассмотрим несколько крутых по мнению автора команд и расскажем почему это круто. Начнем со снипетов.

Полезные снипеты:


Заносим компьютер в TrustedHosts

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

Set-Item WSMan:\localhost\Client\TrustedHosts -Value '192.168.0.1'

Wildcard тоже работает, если не хотите каждый раз добавлять новый хост в TrustedHosts.

Set-Item WSMan:\localhost\Client\TrustedHosts -Value '*'

Храним пароль в зашифрованном виде в файле:

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

Если выполнять скрипты из под зашедшего пользователя, будут использоваться креды этого пользователя.

Read-Host -AsSecureString | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString | Out-File -FilePath .\Password.txt

Забираем зашифрованный пароль из файла:

$Password = Get-Content C:\Password.txt | ConvertTo-SecureString

Тоже самое можно проделать и с Credentials, заменим Read-Host на Get-Credential.

Get-Credential | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString | Out-File -FilePath .\Credential.txt

Получаем дату последней загрузки ОС.

(gcim win32_operatingsystem).LastBootUpTime

Тем же самым способом получаем еще и аптайм.

Get-CimInstance Win32_operatingsystem -ComputerName $computers |
Select-Object LastBootUpTime,
@{Name="Uptime";Expression = {(Get-Date) - $_.LastBootUptime}} 

Получаем список установленных программ:

Именно программ, а не компонентов:


Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* |  Select-Object DisplayName, DisplayVersion, Publisher, InstallDate |
Format-Table –AutoSize


Получаем список дисков, в том числе сетевых и свободное место на них

Get-PSDrive -PSProvider filesystem | where-object {$_.used -gt 0} |
Select-Object -property Root,@{name="Size";expression={($_.used+$_.free)/1GB -as [int]}},
@{name="Used";expression={($_.used/1GB) -as [int]}},
@{name="Free";expression={($_.free/1GB) -as [int]}} 

Останавливаем процесс по его имени.

В этом примере останавливаем Chrome. Wildcard тоже подойдет, если хотите завершить всё.

Get-Process -Name "chrome" | Stop-Process

Копируем настройки доступа к папкам на дочерние папки

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

Копируем ACL у папки:

$Acl = Get-Acl -Path C:\folder\

Вставляем ACL на все файлы и подпапки:

Get-ChildItem -Path C:\Folder\ -Recurse | Set-Acl $Acl 

Полезные скрипты:


Вот тут начинается самое страшное веселье. Если вам нужно провести инвентаризацию, будет очень полезно глянуть. Если ваши компьютеры не в AD, то скрипт станет немного сложнее, итерировать придется по заранее составленному файлу, а пароли нужно будет брать из файла или через Get-Credential. Вся разница:

C AD:

$ADComputers = (Get-ADComputer -filter *).DNSHostName

Без AD:

$Credentials = Get-Credential
$Computers = Get-Content -Path C:\servers.txt

Получаем аптайм дату последней загрузки каждого из компьютеров в AD:


$ADComputers = (Get-ADComputer -filter *).DNSHostName

foreach ($i in $ADComputers) {
 
    Invoke-Command $i {
        Get-CimInstance Win32_operatingsystem |
        Select-Object LastBootUpTime,
        @{Name="Uptime";Expression = {(Get-Date) - $_.LastBootUptime}} 
    } 
 
}

Получаем все установленные программы на всех компьютерах AD:

$ADComputers = (Get-ADComputer -filter *).DNSHostName
foreach ($i in $ADComputers) {
 
    Invoke-Command -computername $i {
        gcim win32_product -computername $env:computername | Sort-Object -property Vendor,Name | Select-Object -property Vendor,Name, Caption 
    } 
 
}

Тоже самое можно будет получить и для компонентов, подставив в Invoke-Command:

Get-WindowsFeature | Where-Object -Property "Installed" -EQ "Installed"

Получаем компьютеры, где запущен нужный нам процесс:

Если вы забыли на каком компьютере запущена та или иная программа, можно получить её таким способом. Рассмотрим на примере браузера:

$ADComputers = (Get-ADComputer -filter *).DNSHostName
foreach ($i in $ADComputers) {
 
    Invoke-Command -computername $i {
        Get-Process -Name "Chrome"  -ErrorAction SilentlyContinue
    } 
 
}

Убить браузер можно будет так же легко, как и получить процесс:

$ADComputers = (Get-ADComputer -filter *).DNSHostName
foreach ($i in $ADComputers) {
 
    Invoke-Command -computername $i {
        Get-Process -Name "Chrome" | Stop-Process -ErrorAction SilentlyContinue
    } 
 
}

Получаем компьютеры, где установлена конкретная программа:

Кокретно в этом случае Java. Поможет при проведении инвентаризации можно будет посмотреть имена компьютеров где она была установлена.

$ADComputers = (Get-ADComputer -filter *).DNSHostName
foreach ($i in $ADComputers) {
 
    Invoke-Command -computername $i {
        gcim win32_product -computername $env:computername | Select-String -Pattern "Java" -AllMatches | Sort-Object -property Vendor,Name | Format-Table -ErrorAction SilentlyContinue
 
    } 
 
}

Если у вас возникнут предложения, будем рады добавить их в эту статью. Надеемся, эти примеры были для вас полезны.

Комментарии (15)


  1. The_Kf
    10.01.2020 13:21

    1. ultra_vds Автор
      10.01.2020 13:49

      Добавили в качестве еще одного способа получения списка установленных программ.
      Спасибо.


      1. The_Kf
        10.01.2020 13:52

        Вы не поняли: Нельзя рекомендовать Win32_Product для этого. Вообще.


        1. ultra_vds Автор
          10.01.2020 19:30

          Исправлено.


  1. Daysleeper
    10.01.2020 13:32
    +1

    (Get-Credential).Password
    скобочку вначале забыл :). плюс нужно добавить один нюанс, в разных версиях 10ки, длина хэша пароля отличается. можно наткнутся на особенность, что на одном компьютере хеш из файла с паролем сработает, а на другом нет.


    1. ultra_vds Автор
      10.01.2020 13:49

      Исправлено.


  1. Evgenym
    10.01.2020 14:29

    Подскажите, пожалуйста, вы пробовали протестировать использование зашифрованного пароля на других ПК или тестили только на своей машине? Насколько я помню, я когда-то пытался сделать подобное и удивился, почему не работает на других компьютерах. Потом, когда стал разбираться, то оказалось, что если попробовать зашифровать пароль так, как указано в статье, то дешифровка возможна только под аккаунтом пользователя, под которым осуществляется шифрование. Для того, чтобы использовать зашифрованный пароль из файла на другой машине/под другим пользователям, то нужно указывать AES-ключ через параметр -Key.

    В статье ниже рассматривают подобные варианты:
    https://www.pdq.com/blog/secure-password-with-powershell-encrypting-credentials-part-2/


    1. ultra_vds Автор
      10.01.2020 14:33

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


  1. Sergani
    10.01.2020 18:06

    Может я устарел, но синтаксис powershell для меня ужасен, как язык инопланетян. Использую для автоматизации JScript и VBScript, иногда BAT файлы. Не вижу ни одной причины для изучения powershell


    1. ultra_vds Автор
      10.01.2020 19:25
      +1

      Ну как можно не любить Powershell?
      (????)?????


  1. geekimho
    10.01.2020 19:20

    Благодарствую!


  1. 4ervyak
    10.01.2020 22:52
    +1

    Invoke-Command c gcim в цикле для большого количества машин( например, если AD сильно большая) достаточно «страшная» вещь, т.к висящий RPC, например- вешает цикл. Есть отличный коммандлет Start-Job. Можно сильно распараллелить выполнение и цикл не будет зависеть от одной «проблемной» машины


  1. Coob
    11.01.2020 01:48

    Обожаю PowerShell за возможность работать с .net библиотеками (включая поддержку работы с приватными полями и методами)
    https://blog.netspi.com/using-powershell-and-reflection-api-to-invoke-methods-from-net-assemblies/
    Это ооочень расширяет возможности.


  1. vagon333
    11.01.2020 06:51

    По теме обсуждения PowerShell, вчера была интересная презентация кросс-платформенного PowerShell 7 на базе .Net Core.
    Очень любопытно: создавали ВМ-ки, KeyVault для хранения credentials, онлайновый Storage и переброска файлов в него из локалки.
    Кстати, PS скрипты прекрасно редактируются и дебажатся из бесплатной кроссплатформенной VS Code.

    github.com/dave-007/Take-Command-of-Azure-PowerShell-PowerShell-7
    www.meetup.com/mcsfug/events/267540026


  1. Sergey-S-Kovalev
    11.01.2020 08:27

    $ADComputers = (Get-ADComputer -filter *).DNSHostName
    Кто это пытается использовать очень быстро понимает, что:
    1. Нужно проверять доступность, иначе ждем пока не отвалится по таймауту.
    2. Использовать Jobы, потому что есть тысяча причин по которой запрос повиснет мертвым грузом, плюс распараллеливание процесса.
    3. Нужно проверять что за DNSHostName скрывается именно тот хост, который нужен, ибо DNS может указывать на IP который занял уже другой хост при использовании DHCP, либо при не настроенной очистке DNS записей.

    ну и из общего совета: иногда быстрее и надежнее сделать через schtasks c последовательным созданием/выполнением/удалением задания. Менее удобно в части получения результатов выполнения команды, но не зависит от WinRM и версии винды и повершелки.