Содержание

Active Directory

Показать значение атрибута msDS-cloudExtensionAttribute1 текущего пользователя

# Import AD Module
Import-Module ActiveDirectory
get-aduser $env:UserName -Properties * | Select msDS-cloudExtensionAttribute1
# Имя компа:
# $env:computername
# Имя домена:
# $env:DomainName

Вывести список пользователей и показать логин и msDS-cloudExtensionAttribute1

# Из OU
get-aduser -Filter * -SearchBase "OU=Руководство,DC=domain,DC=local" -Properties Enabled,msDS-cloudExtensionAttribute1 | select Name,SamAccountName,msDS-cloudExtensionAttribute1 | ft -AutoSize
# По группе
Get-ADGroupMember "CN=Заместители директоров,OU=Groups,DC=domain,DC=local" | get-aduser -Properties Enabled,msDS-cloudExtensionAttribute1 | select Name,SamAccountName,msDS-cloudExtensionAttribute1 | ft -AutoSize

Извлечь атрибут msDS-CloudExtensionAttribute1 у пользователей "Рогов и копыт", и добавить значения в группу "Компьютеры для тестирования"

get-aduser -Filter {Company -eq "Рога и копыта"} -SearchBase "OU=Users,DC=domain,DC=local" -Properties * | Select -expand msDS-CloudExtensionAttribute1 | foreach {Add-ADGroupMember -Identity "CN=Компьютеры для тестирования,OU=Группы компьютеров,OU=Resource,DC=domain,DC=local" -Members (Get-ADComputer $_)}

Извлечь компы из выгрузки из KSC и поместить их в соответствующие группы в AD для установки новых версий Хрома

$comps = Import-Csv "C:\Users\user\Desktop\Chrome.csv" -Delimiter "`t" |select -expand "Имя компьютера" |
% {Get-ADComputer $_ -properties operatingsystem}
 
$legacy,$new = $comps.where({$_.operatingsystem -match "XP|2003"}, 'Split')
"Компьютеры для установки Google Chrome (Windows XP)" |Add-ADGroupMember -Members $legacy
"Компьютеры для установки Google Chrome" |Add-ADGroupMember -Members $new

Имя компьютера, время входа в AD + файл

# write to file
$file = "\\server\logons\$env:USERNAME.csv"
$exist = Test-Path $file
$t = get-date -Format "yyyy.MM.dd;HH:mm:ss"
if ($exist -ne $true) {echo "Дата;Время;Логин;Компьютер" |Out-File $file -Encoding utf8}
echo "$t;$env:USERNAME;$env:COMPUTERNAME" |Out-File $file -Encoding utf8 -Append
 
# write last logged-on comp name to msds-cloudextensionattribute10
$Searcher = New-Object DirectoryServices.DirectorySearcher
$Searcher.Filter = "(&(objectCategory=person)(anr=$env:username))"
$Searcher.SearchRoot = 'LDAP://DC=domain,DC=ru'
$path = $Searcher.FindOne() |select -expand path
$user = [ADSI]"$path"
$user.Put("msds-cloudextensionattribute10", "$env:computername")
# write last bootup time to msds-cloudextensionattribute11
$boottime = Get-WmiObject win32_operatingsystem
$boottime = $boottime.ConverttoDateTime($boottime.lastbootuptime)
$boottime = $boottime.toString("dd.MM.yyyy HH:mm:ss")
$user.Put("msds-cloudextensionattribute11", "$boottime")
$user.SetInfo()

На пользовательских машинах не установлены RSAT, поэтому

Set-ADUser $env:username -Add @{'msDS-cloudExtensionAttribute1'="$env:computername"}

не пройдёт.

Дополнительно - примеры запросов к AD:

#user properties
$san = 'user'
$getad = (([adsisearcher]"(&(objectCategory=User)(samaccountname=$san))").findall()).properties
$getad
 
#Computer properties
$pc = 'computername'
$getad = (([adsisearcher]"(&(objectCategory=Computer)(name=$pc))").findall()).properties
$getad

https://social.technet.microsoft.com/Forums/Lync/en-US/fd0b0a1c-b6dc-4657-8a95-21b6a11377df/using-ad-module-without-loading-rsat?forum=winserverpowershell
https://social.technet.microsoft.com/wiki/contents/articles/4231.working-with-active-directory-using-powershell-adsi-adapter.aspx
https://social.technet.microsoft.com/Forums/en-US/646ffe45-5ba5-461c-895f-a557e35cbc0d/need-to-set-extensionattribute1-of-an-adgroup-through-powershell

Найти пользователя в AD по части ФИО и показать информацию

[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)]
$user
)
 
$cusers = get-aduser -Filter "(ObjectClass -eq 'user') -AND (SamAccountName -notlike '*.crm')" -SearchBase "dc=domain,dc=ru" |? Name -like "*$user*"
foreach ($cuser in $cusers) {
Get-ADUser $cuser -Properties * | 
select @{n="ФИО"; e={$_.Name}}, `
@{n="Логин"; e={$_.sAMAccountName}}, `
@{n="Кабинет"; e={$_.physicalDeliveryOfficeName}}, `
@{n="Телефон"; e={$_.telephoneNumber}}, `
@{n="Компания"; e={$_.'msDS-cloudextensionAttribute1'}}, `
@{n="Управление"; e={$_.'msDS-cloudextensionAttribute2'}}, `
@{n="Дирекция"; e={$_.'msDS-cloudextensionAttribute3'}}, `
@{n="Отдел"; e={$_.'msDS-cloudextensionAttribute4'}}, `
@{n="Служба"; e={$_.'msDS-cloudextensionAttribute5'}}, `
@{n="Группа"; e={$_.'msDS-cloudextensionAttribute6'}}, `
@{n="Должность"; e={$_.'msDS-cloudextensionAttribute7'}}, `
@{n="Имя компьютера"; e={$_.'msDS-cloudextensionAttribute10'}}, `
@{n="Время последней загрузки"; e={$_.'msDS-cloudextensionAttribute11'}}
}
pause

Генератор паролей

$length = 32
[int]$quarter = $length/4
[int]$residual = $length-$quarter*2
# if special are used
# [int]$residual = $length-$quarter*3
 
$cletters = [char[]](0..255) -clike '[A-Z]' -notmatch "I|O|B" # except I, O, B
$letters = [char[]](0..255) -clike '[a-z]' -notmatch "l" # except l
$numbers = [char[]](0..255) -clike '[0-9]' -notmatch "1|0|8" # except 1, 0, 8
# $special = @("!","-","#","$","%","^","&","_",".",";") # add more if you need
 
 
$rcletters = Get-Random -InputObject $cletters -count $quarter
$rnumbers = Get-Random -InputObject $numbers -count $quarter
# $rspecial = Get-Random -InputObject $special -count $quarter
$rletters = Get-Random -InputObject $letters -count $residual
 
$password = $rcletters + $rletters + $rnumbers
# $password = $rcletters + $rletters + $rnumbers + $rspecial
$password = (Get-Random -InputObject $password -count $length) -join ""
$password
# x = length in characters
# y = minimum number of non-alphanumeric characters
Add-Type -AssemblyName System.web
[System.Web.Security.Membership]::GeneratePassword(x,y)

ФИО в логин

$src = Import-Excel 'C:\scripts\Список сотрудников.xlsx' -StartRow 4 -StartColumn 2 |select -expand ФИО
 
$alldata = @()
 
foreach ($fio in $src) {
$login = $fio -split " "
$f = $login[0]
$i = ($login[1]).substring(0,1)
$o = ($login[2]).substring(0,1)
$login = $f + $i + $o
 
# ьи, ье, ьё - Ильин, yuyu, yaya - Юлия Юрьевна
$login = $login -replace 'ий','y' -replace 'ый','y' -replace 'ьи','yi' -replace 'ье','ye' -replace 'ьё','ye' `
-replace 'а','a' -replace 'б','b' -replace 'в','v' -replace 'г','g' -replace 'д','d' -replace 'е','e' `
-replace 'ё','e' -replace 'ж','zh' -replace 'з','z' -replace 'и','i' -replace 'й','y' -replace 'к','k' `
-replace 'л','l' -replace 'м','m' -replace 'н','n' -replace 'о','o' -replace 'п','p' -replace 'р','r' `
-replace 'с','s' -replace 'т','t' -replace 'у','u' -replace 'ф','f' -replace 'х','kh' -replace 'ц','ts' `
-replace 'ч','ch' -replace 'ш','sh' -replace 'щ','sch' -replace 'ь' -replace 'ы','y' -replace 'ъ' `
-replace 'э','e' -replace 'ю','yu' -replace 'я','ya' -replace 'yuyu','yy' -replace 'yaya','yy' `
-join ""
 
    $raw = [pscustomobject]@{
    FIO = $fio
    Login = $login
    }
$alldata += $raw
}
$alldata
 
# $alldata |Export-Csv c:\temp\logins.csv -Delimiter ';' -Encoding utf8 -NoTypeInformation

Скопировать группы, содержащие "КТ " из одной учётки в другую

[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)]
$from,
[Parameter(Mandatory=$True)]
$to
)
$CopyFromUser = Get-ADPrincipalGroupMembership $from |where {$_.name -like '*КТ *'}
$CopyToUser = Get-ADPrincipalGroupMembership  $to
$CopyFromUser | Where{$CopyToUser -notcontains $_} | Add-ADGroupMember -Member $to

Добавить комп в группу

[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)]
$comp
)
add-ADGroupMember -Identity "Компьютеры для установки КриптоПро CSP 4.0.9944" -Members (get-adcomputer $comp)
 
# Добавить много компов
"Имя группы" |Add-ADGroupMember -Members `
("pc1722","pc1723","pc1724","pc1725","pc1726","pc1727","pc1728","pc1729","pc1730","pc1731" |Get-ADComputer)

Показать включённых юзеров с датой входа более 2 недель

get-aduser -SearchBase "OU=Административный департамент,OU=Users,domain,DC=ru" -Properties lastlogondate -Filter {enabled -eq $true} |
? Lastlogondate -lt (Get-Date).Adddays(-14) |sort lastlogondate -Descending |ft name,lastlogondate

Лог входов в систему

$file = "\\server\logon-log\$env:USERNAME.csv"
$exist = Test-Path $file
$t = get-date -Format "yyyy.MM.dd;HH:mm:ss"
 
if ($exist -ne $true ) {
echo "Дата;Время;Логин;Компьютер" |Out-File $file -Encoding utf8
}
 
echo "$t;$env:USERNAME;$env:COMPUTERNAME" |Out-File $file -Encoding utf8 -Append

Запуск из GPO:

%WINDIR%\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -File \\path-to-server\scripts\logon-log.ps1

Список учёток с неправильно заполненными инициалами

Get-ADUser -filter 'name -like "*на" -or name -like "*ич"' -SearchBase "OU=1_Users,OU=test,DC=domain,DC=ru" -Properties initials |
? initials -notmatch '\w\. \w\.' |select -expand name |sort

Поставить галку "Защитить объект от случайного удаления" для всех OU домена

Get-ADOrganizationalUnit -filter * |Set-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $true

Настройка локальных машин

Отключение энергосбережения для сетевого адаптера

# Disable "Allow the computer to turn off the device to save power"
foreach ($id in (gwmi win32_networkadapter |? {$_.physicaladapter -and $_.name -notmatch "virtual|wireless|microsoft" -and $_.Manufacturer -ne "Microsoft"}).deviceid) {
Set-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4d36e972-e325-11ce-bfc1-08002be10318}\$('{0:d4}' -f [int]$id)" -Name PnPCapabilities -Value 24
}
 
# Вариант выборки сетевого адаптера:
# gwmi Win32_NetworkAdapter |? {$_.PNPDeviceID -notlike "ROOT\*" -and $_.Manufacturer -ne "Microsoft" -and $_.ConfigManagerErrorCode -eq 0}

https://support.microsoft.com/en-us/help/2740020/information-about-power-management-setting-on-a-network-adapter
https://techcommunity.microsoft.com/t5/exchange-team-blog/do-you-have-a-sleepy-nic/ba-p/590996
https://gallery.technet.microsoft.com/scriptcenter/Disable-turn-off-this-f74e9e4a

Информация по TCP-соединениям

$comps = "PC1708","PC1163"
 
foreach ($comp in $comps) {
    Invoke-Command -ComputerName $comp -ScriptBlock {
        netstat -vb
    } |Out-File c:\temp\$comp.txt
}
 
foreach ($comp in $comps) {
    "--- $comp ---"
    (gc c:\temp\$comp.txt) -match '\[.*\]' |group
}
--- PC1708 ---
Count Name                      Group                                                                                                                                               
----- ----                      -----                                                                                                                                               
   33  [svchost.exe]            { [svchost.exe],  [svchost.exe],  [svchost.exe],  [svchost.exe]...}                                                                                 
    1  [1cv8c.exe]              { [1cv8c.exe]}                                                                                                                                      
  492  [Iwproxy.exe]            { [Iwproxy.exe],  [Iwproxy.exe],  [Iwproxy.exe],  [Iwproxy.exe]...}                                                                                 
    1  [1cv8.exe]               { [1cv8.exe]}                                                                                                                                       
    5  [OUTLOOK.EXE]            { [OUTLOOK.EXE],  [OUTLOOK.EXE],  [OUTLOOK.EXE],  [OUTLOOK.EXE]...}                                                                                 
  244  [chrome.exe]             { [chrome.exe],  [chrome.exe],  [chrome.exe],  [chrome.exe]...}                                                                                     
    4  [IWDeployAgent.exe]      { [IWDeployAgent.exe],  [IWDeployAgent.exe],  [IWDeployAgent.exe],  [IWDeployAgent.exe]}                                                            
    6  [iwdmc.exe]              { [iwdmc.exe],  [iwdmc.exe],  [iwdmc.exe],  [iwdmc.exe]...}                                                                                         
    2  [iwshcs.exe]             { [iwshcs.exe],  [iwshcs.exe]}                                                                                                                      
    2  [LMS.exe]                { [LMS.exe],  [LMS.exe]}                                                                                                                            
    2  [DM.Client.exe]          { [DM.Client.exe],  [DM.Client.exe]}                                                                                                                
--- PC1163 ---
  456  [Iwproxy.exe]            { [Iwproxy.exe],  [Iwproxy.exe],  [Iwproxy.exe],  [Iwproxy.exe]...}                                                                                 
    3  [OUTLOOK.EXE]            { [OUTLOOK.EXE],  [OUTLOOK.EXE],  [OUTLOOK.EXE]}                                                                                                    
    1  [spoolsv.exe]            { [spoolsv.exe]}                                                                                                                                    
    1  [1cv8c.exe]              { [1cv8c.exe]}                                                                                                                                      
    1  [1cv8.exe]               { [1cv8.exe]}                                                                                                                                       
  228  [chrome.exe]             { [chrome.exe],  [chrome.exe],  [chrome.exe],  [chrome.exe]...}                                                                                     
    4  [IWDeployAgent.exe]      { [IWDeployAgent.exe],  [IWDeployAgent.exe],  [IWDeployAgent.exe],  [IWDeployAgent.exe]}                                                            
    6  [iwdmc.exe]              { [iwdmc.exe],  [iwdmc.exe],  [iwdmc.exe],  [iwdmc.exe]...}                                                                                         
    2  [iwshcs.exe]             { [iwshcs.exe],  [iwshcs.exe]}                                                                                                                      
    2  [DM.Client.exe]          { [DM.Client.exe],  [DM.Client.exe]}    

Таблички, отчёты, массивы

+= удаляет старый и создаёт новый массив, т. к. он фиксированного размера, и при большом кол-ве элементов работает очень медленно.
Лучше использовать [System.Collections.ArrayList] или [System.Collections.Generic.List[]]

https://batishchev.ru/blog/all/arraylist-i-proizvoditelnost-massivov-v-powershell/
https://adamtheautomator.com/powershell-array/

$arrayList = [System.Collections.ArrayList]::new()
$arrayList.add("aaa") > $null

[System.Collections.Generic.List[]] - универсальный список с возможностью указания типа массива. Что удобно - в отличие от ArrayList метод Add не возвращает значение, поэтому для него не нужно выполнять void.

$a = [System.Collections.Generic.List[int]]::new() # числовой массив
$a = [System.Collections.Generic.List[string]]::new() # строковый массив
$a = [System.Collections.Generic.List[object]]::new() # любой тип объектов

Добавить строку в отчёт с двумя столбцами

$report += New-Object -TypeName psobject -Property @{folder="Всего каталогов"; sizeMB="$($report.folder.count)"}

Цвет затенения для строк

Для использования в html для фонового цвета перемежающихся строк

Массив русского алфавита с индексом

Почему не [char[]](0..65535) -clike '[а-я]' - там не будет буквы ё. Поэтому

$alfabetRus = @("а","б","в","г","д","е","ё","ж","з","и","й","к","л","м","н","о",
"п","р","с","т","у","ф","х","ц","ч","ш","щ","ъ","ы","ь","э","ю","я")
$alfabetRus |select @{n="letter";e={$_}},@{n="index";e={$alfabetRus.indexof($_)+1}}
 
letter index
------ -----
а          1
б          2
в          3
...

Можно вычислить индексы символов и потом вставить их в массив для выбора

$allchars = [char[]](0..65535)
($allchars |select @{n="letter";e={$_}},@{n="index";e={$allchars.indexof($_)}}) -cmatch '[а-яё]'
 
# А-Я
1140..1071
# а-я
1072..1103
# Ё,ё
1025,1105
# A-Z
65..90
# a-z
97..122
 
$alfabetRus = [char[]](1072..1077+1105+1078..1103)
$alfabetRus |select @{n="letter";e={$_}},@{n="index";e={$alfabetRus.indexof($_)+1}}

Разбить массив на части определённого размера

$list = 1..1018
$chunkSize = 100
 
$c = [pscustomobject]@{Value = 0}
$groups = $list |group {[math]::Floor($c.Value++ / $chunkSize)}
 
$groups[0].group

https://stackoverflow.com/questions/13888253/powershell-break-a-long-array-into-a-array-of-array-with-length-of-n-in-one-line

Файловые операции

Удалять из папки все файлы старше текущего месяца, кроме файлов от 1, 15 и последнего дня месяца

$path="\\server\backups\folder"
$files=get-childitem $path
$currentmonth=(get-date).Month
foreach ($file in $files) 
{
if (($file.lastwritetime).Month -ne $currentmonth -and ($file.lastwritetime).day -ne 15 -and ($file.lastwritetime).day -ne 1 -and ($file.lastwritetime).Month -eq (($file.lastwritetime).adddays(1)).Month)
    {
    Remove-Item $path\$file -Force -Recurse
    }
}

Удалить все файлы, кроме последних 10

gci | sort CreationTime | select -skip 10 | Remove-item -Force

Пакетное переименование файлов

Дано:
Pet Shop Boys - Happiness-47208322.mp3
Pet Shop Boys - The Pop Kids-47208325.mp3

[int]$n = 1
$files = dir 'D:\music\Pet Shop Boys - Super (2016)' -File |sort creationtime
foreach($file in $files) {
$a = $n.ToString("00")
Rename-Item $file -NewName ($file.name -replace "^","$a ")
$n = $n+1
}
dir |Rename-Item -NewName {$_.name -replace "-\d+"}
 
# сделать плейлист с именем как у текущего каталога
$plsname = (Get-ItemProperty .).name
(dir -file *.mp3).name |out-file .\"$plsname.m3u" -Encoding UTF8

Результат:
01 Pet Shop Boys - Happiness.mp3
02 Pet Shop Boys - The Pop Kids.mp3

https://devblogs.microsoft.com/scripting/hey-scripting-guy-how-can-i-use-leading-zeroes-when-displaying-a-value-in-windows-powershell/

Дано:
1 - Introduction.mp4
10 - Lesson 1 Lab.mp4
100 - Understanding Why Decoupling is Important.mp4

$files = dir 'D:\Видео\Video Collection'
 
foreach ($file in $files) {
$num,$rest = $file.name -split ' - ',2
Rename-Item $file.FullName -NewName "$(([int]$num).ToString("000")) - $rest" #-WhatIf
}

Результат:
001 - Introduction.mp4
010 - Lesson 1 Lab.mp4
100 - Understanding Why Decoupling is Important.mp4

Сортировка

Если отсортировать файлы вида file1.txt - file100.txt, то выйдет

file1.txt
file10.txt
file11.txt
...
file18.txt
file19.txt
file2.txt
file20.txt
file21.txt
и т. д.

Чтобы отсортировать по-человечески:

dir "C:\temp\folder\*.txt" |sort {[int]($_.basename -replace '\D')}

Посмотреть квоты на файловом сервере

Get-FSRMQuota |? path -Match 'отдел' |select @{n='Путь';e={$_.path}},@{n='Использовано (ГБ)';e={$_.usage / 1GB -as [int]}},@{n='Всего (ГБ)';e={$_.size / 1GB -as [int]}}

Ожидание снятия блокировки файла и копирование

function Test-FileLock {
  param (
    [parameter(Mandatory=$true)][string]$Path
  )
  $oFile = New-Object System.IO.FileInfo $Path
  if ((Test-Path -Path $Path) -eq $false) {
    return $false
  }
  try {
    $oStream = $oFile.Open([System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None)
    if ($oStream) {
      $oStream.Close()
    }
    return $false
  } catch {
    return $true # file is locked by a process
  }
}
 
$from = '\\example.com\share\users\Mihailov-20230320.pst'
$to = '\\pc01\c$\Users\Soloviev\Documents\Файлы Outlook'
 
do {
    sleep 30
}
until (-not (Test-FileLock $from))
 
Write-Host -fore green "Copying..."
cp $from $to
 
# Test-Path "$to\$((Get-Item $from).name)"

https://stackoverflow.com/questions/24992681/powershell-check-if-a-file-is-locked

Создание тестовых файлов с датами изменения и создания в прошлом

$t = Get-Date
$folder = "D:\temp\from"
 
1..100 |% {
    $fileTime = $t.AddDays(-$_)
    $fileBaseName = $fileTime.ToString("yyyy.MM.dd")
    $filePath = "$folder\$fileBaseName.txt"
 
    $file = New-Item $filePath -ItemType File
    Set-Content $filePath -Value $fileBaseName
 
    $file.CreationTime = $fileTime
    $file.LastWriteTime = $fileTime
}

Удалить файл из корзины

$file = 'C:\temp\out — копия (3).txt'
 
(New-Object -ComObject Shell.Application).NameSpace(0x0a).Items() |? {
($_.ExtendedProperty("{9B174B33-40FF-11D2-A27E-00C04FC30871} 2") + '\' + $_.name + '.' + ($_.path -replace '.*\.')) -eq $file
} |del

Работа с текстом

Программа для создания большого файла txt: Dummy File Creator.

Конвертировать строку во время

$folder = "D:\temp\20211025"
$reportDate = [datetime]::parseexact($folder.Split("\")[-1], 'yyyyMMdd', $null)
 
25 октября 2021 г. 0:00:00

https://stackoverflow.com/questions/38717490/convert-a-string-to-datetime-in-powershell

Убрать из файла все строки, содержащие плюс

(Get-Content "C:\temp\test.txt") -notmatch '\+' |Set-Content "C:\temp\test-out.txt"

http://stackoverflow.com/questions/39828780/delete-lines-containing-specific-character-in-text-file

Каждое слово с большой буквы

(Get-Culture).TextInfo.ToTitleCase('текст, каждое слово которого будет начинаться с большой буквы')

Фильтр отчётов Cobian Backup

cd D:\Downloads
Expand-Archive .\Журналы.zip
cd .\Журналы
gc *.txt |Select-String ERR |out-file -width 30000 .\errors.txt
Invoke-Item .\errors.txt

Выбрать каждую N строку в файле

# каждую 1, 5, 9, 13, 17 и т. д.
gc "D:\temp\test.txt" |? {($_.ReadCount % 4) -eq 1}

Замена ошибок в тексте (afterscan)

FIXME

-replace '([\.\,\?\!])(\w)','$1 $2' ` # Добавить пробел между знаками препинания и следующим словом
-replace '\s+([\.\,\?\!])','$1' ` # Убрать пробел между словом и знаком препинания
-replace "\s+$" ` # Убрать пробелы в конце строки
-replace "…","..." ` # Заменить символ троеточия на реальные три точки
-replace '(\d)(\D)','$1 $2' ` # Добавить пробел между числом и следующим за ним словом
-replace "\.$",".`n" # Добавить пустую строку, если строка заканчивается точкой
 
-replace '(?<=\d)(\s+)?([\.\:])(\s+)?(\d+)','$2$4' # Убрать пробелы в датах (где между цифрами . и :)

Обработка больших файлов

Надо применять методы .NET и упаковывать regex, т. к. get-content очень медленный.

function ExtractTo-Chunk ($in,$out,$regex) {
    [regex]$regex = $regex
    [System.IO.File]::AppendAllText("$out", ([System.IO.File]::ReadAllLines("$in") -match $regex -join [Environment]::NewLine))
}
 
ExtractTo-Chunk -in "C:\temp\big.log" -out "C:\temp\chunk1.log" -regex "192.168.1.10"
ExtractTo-Chunk -in "C:\temp\big.log" -out "C:\temp\chunk1.log" -regex "10.0.0.8"

Reading large text files with Powershell
Speed issue on -match operation for large text file
PowerShell scripting performance considerations

Быстрое чтение файла в массив:

[io.file]::readalllines("C:\temp\file.txt")
[io.file]::readalllines("C:\temp\file.txt",[System.Text.Encoding]::UTF8)
[io.file]::readalllines("C:\temp\file.txt",[System.Text.Encoding]::GetEncoding('windows-1251'))

Склонение слов в предложении с числительным

function Get-Declension ($pre1,$n,$w1,$w2,$w5) {
    switch -Regex ($n) {
        '(?<!1)1$'     {$p = $pre1 -replace ''; $w = $w1}
        '(?<!1)[234]$' {$p = $pre1 -replace '(а|(?<![ао]))$','о'; $w = $w2}
        default        {$p = $pre1 -replace '(а|(?<![ао]))$','о'; $w = $w5}
    }
if ($pre1) {"$p $n $w"}
else {"$n $w"}
}
 
"Машина проехала $(Get-Declension -n 2 -w1 километр -w2 километра -w5 километров)"
"Одобрено $(Get-Declension -n 101 -w1 обновление -w2 обновления -w5 обновлений)"
"Всего $(Get-Declension -n 22 -w1 "активный пользователь" -w2 "активных пользователя" -w5 "активных пользователей")"
"$(Get-Declension -pre1 Удален -n 221 -w1 "каталог" -w2 "каталога" -w5 "каталогов")"
"$(Get-Declension -pre1 Удалена -n 11 -w1 "папка" -w2 "папки" -w5 "папок")"
"$(Get-Declension -pre1 Посажен -n 2511 -w1 "дуб" -w2 "дуба" -w5 "дубов")"
"$(Get-Declension -pre1 Разбито -n 4 -w1 "яйцо" -w2 "яйца" -w5 "яиц")"
Машина проехала 2 километра
Одобрено 101 обновление
Всего 22 активных пользователя
Удален 221 каталог
Удалено 11 папок
Посажено 2511 дубов
Разбито 4 яйца

Список модулей PHP из буфера по алфавиту и с переносом строки

$list = (Get-Clipboard) -replace '\s+\\$' |sort
$total = $list.count
$c = 1
$list |% {
    if ($c -eq $total) {$_}
    else {$_ + " \"}
$c++
} |Set-Clipboard

Исходный список

php81-fpm \
php81-phar \
php81-iconv \
php81-mbstring \
php81-openssl \
php81-curl \
php81-dom \
php81-pdo_mysql \
php81-exif \
php81-pcntl \
php81-bcmath \
php81-ctype \
php81-gd \
php81-soap \
php81-zip \
php81-sockets \
php81-session \
php81-fileinfo \
php81-tokenizer \
php81-simplexml \
php81-xml \
php81-xmlreader \
php81-xmlwriter
php81-redis

Результат

php81-bcmath \
php81-ctype \
php81-curl \
php81-dom \
php81-exif \
php81-fileinfo \
php81-fpm \
php81-gd \
php81-iconv \
php81-mbstring \
php81-openssl \
php81-pcntl \
php81-pdo_mysql \
php81-phar \
php81-redis \
php81-session \
php81-simplexml \
php81-soap \
php81-sockets \
php81-tokenizer \
php81-xml \
php81-xmlreader \
php81-xmlwriter \
php81-zip

Замена многострочного блока

Управление групповой политикой

body { font-size:68%;font-family:MS Shell Dlg; margin:0px,0px,0px,0px;
border: 1px solid #666666; background:#F6F6F6; width:100%;
...
overflow:visible; } button { display:none; } .head { color:#000000;
background:#FFFFFF; border:1px solid #000000; } }

Путь размещения параметров:

Объяснение
(gc 'D:\Downloads\file.txt' -Raw) -replace 'body((.|\n)*)\} \}'

Замена нескольких пустых строк на одну

(gc 'D:\Downloads\file.txt' -Raw) -replace '(\r?\n){2,}', '$1$1'

Преобразование строк в таблицу

Opera Hotlist version 2.0
Options: encoding = utf8, version=3

#CONTACT
	ID=11
	NAME=Justynka
	CREATED=1195505237
	MAIL=JUSTYNA66@gmail.com
	ICON=Contact0

#CONTACT
	ID=12
	NAME=Leszek
	CREATED=1195677687
	MAIL=Leszek@domena.pl
	ICON=Contact0

#CONTACT
	ID=13
	NAME=Iwona Kwiatkowska
	CREATED=1196277590
	MAIL=iwon.kwiat@op.pl
	ICON=Contact0


#FOLDER
	ID=15
	NAME=Kosz
	CREATED=1195505227
	TRASH FOLDER=YES
	UNIQUEID=EAF22324295C86499476802CC76DE41E

-

#CONTACT
	ID=16
	NAME=Ania
	CREATED=1195505237
	MAIL=Ania.Nowak@poczta.com
	ICON=Contact0

Вычистить ненужные строки и мусор, разделить текст на сегменты, создать структурированные объекты. Разделителем для ConvertFrom-StringData должен быть =.

(Get-Content .\opera.adr -Raw) -replace '^Opera[\s\S]*?#CONTACT' -split '#CONTACT|#FOLDER' |% {
  $props = ConvertFrom-StringData -StringData ($_ -replace '\n-\s+')
  New-Object PSOBject -Property $props |select Name, Mail
}

https://powershellmagazine.com/2014/09/09/using-the-convertfrom-string-cmdlet-to-parse-structured-text/
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/convertfrom-stringdata?view=powershell-5.1

Графика

Диаграммы

На примере ежечасно снимаемых логов вошедших по VPN пользователей.

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms.DataVisualization
 
$Chart = New-Object -TypeName System.Windows.Forms.DataVisualization.Charting.Chart
$Chart.Size = '800,400'
 
$ChartArea = New-Object -TypeName System.Windows.Forms.DataVisualization.Charting.ChartArea
$ChartArea.AxisX.Title = 'Часы'
$ChartArea.AxisY.Title = 'Кол-во пользователей'
$ChartArea.AxisX.Interval = '1'
$ChartArea.AxisX.LabelStyle.Enabled = $true
$ChartArea.AxisX.LabelStyle.Angle = 90
$ChartArea.AxisX.MajorGrid.Enabled = $false # No grid
$ChartArea.AxisY.MajorGrid.Enabled = $false # No grid
$Chart.ChartAreas.Add($ChartArea)
$Chart.Series.Add('VPNUsers') > $null
$Chart.Series['VPNUsers'].IsValueShownAsLabel = $true # Show values on columns' tops
 
# Chart type - Column
$Chart.Series['VPNUsers'].ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Column
 
# Looping files
$txtFiles |% {
$hour = $_.BaseName -replace ".*-" -replace "\d{4}$"
$usersCount = (gc $_.FullName |% {($_ -split "'")[1]} |sort -Unique).count
$Value = $Chart.Series['VPNUsers'].Points.AddXY("$hour","$usersCount")
}
 
# Title
$Title = New-Object -TypeName System.Windows.Forms.DataVisualization.Charting.Title
$Chart.Titles.Add($Title)
$Chart.Titles[0].Text = 'Статистика по часам'
 
# Saving PNG file
$png = "$env:temp\VPNUsersChart.png"
$Chart.SaveImage("$png", "PNG")

https://www.powershellbros.com/building-first-chart-report-powershell/
https://learn-powershell.net/2016/09/18/building-a-chart-using-powershell-and-chart-controls/
https://stackoverflow.com/questions/6413182/how-to-display-values-in-asp-net-chart-control
https://stackoverflow.com/questions/1058572/hiding-the-gridlines-on-an-asp-net-chart-control/1368853

Интернет

Скачать файлы по списку

$url = "https://reasonstation.nl/downloads/yamaha/mp3-midi/"
$pattern = ".MID"
(curl $url).links |select -expand href |Select-String -SimpleMatch "$pattern" |
% {
$f = $url + $_
curl $f -OutFile "$env:userprofile\Downloads\$_"
}

Отправить письмо

Без запроса пароля.

# Сохранение шифрованного пароля в файл
Read-Host -AsSecureString | ConvertFrom-SecureString | Out-File C:\temp\securestring.txt
 
$pass = cat C:\temp\securestring.txt | convertto-securestring                                                            
$mycred = new-object -typename System.Management.Automation.PSCredential -argumentlist "username",$pass
# С Яндекса
Send-MailMessage -SmtpServer smtp.yandex.ru -UseSsl -Encoding utf8 -From "username@yandex.ru" -Subject "Test message" -Body "Body of test message" -To "username@domain.com" -Credential $mycred

Игнорировать самоподписанный сертификат SMTP

[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { return $true }
Send-MailMessage -SmtpServer "mail.example.com" -from "script@example.com" -to "user@example.com" -Subject "Hello" -body "<H1>Hello</H1>" -BodyAsHtml -Encoding UTF8 -UseSsl

Письмо с картинкой в тексте

Send-MailMessage -To "testovtt@domain.ru" -From "birthday@domain.ru" `
-SmtpServer "mail.domain.ru" -Subject "С днём рожденья!" `
-Body "
<h3>Уважаемый Тест Тестович!</h3>
<p>Поздравляю вас с днём рождения!</p>
<img src='cid:birthday.jpg'>
<p><i>С уважением, скрипт.</i></p>
" -Attachments "C:\scripts\birthday.jpg" -BodyAsHtml -Encoding utf8

https://stackoverflow.com/questions/52837468/embed-images-in-email-and-send-via-powershell

Сколько дней осталось до окончания действия SSL-сертификата

С помощью curl (надо скачивать с сайта, встроенный в винду многого не умеет)

$urls = @(
"google.com"
"mail.ru"
"yandex.ru"
"kubernetes.io"
)
#$urls = gc "C:\temp\sites.txt" |sort
$t = get-date
 
function Decode-UnicodeEscaped {
param([string]$str)
# decode if unicode escaped strings
# https://stackoverflow.com/questions/18450218/unescape-unicode-string-powershell
[Regex]::Replace($str, "\\[Uu]([0-9A-Fa-f]{4})", 
{[char]::ToString([Convert]::ToInt32($args[0].Groups[1].Value, 16))} )
}
 
$report = @()
$urls |% {
$dump = & C:\scripts\curl\curl.exe "https://$_" -Iv --stderr -
$issuer = $dump -match "issuer:" -replace ".+: "
$expdate = $dump -match "expire date:" -replace ".+: " -replace "\s+"," "
$expdate = Get-Date $("{0} {3} {1} {2} {4}" -f "$expdate".Split())
    $obj = [pscustomobject]@{
    Сайт = $_
    'Конец срока действия' = $expdate
    'Осталось дней' = if ($expdate) {($expdate - $t).days};
    'УЦ' = Decode-UnicodeEscaped $issuer
    }
$report += $obj
Remove-Variable dump,expdate,issuer
}
$report |sort 'Конец срока действия'

https://nickjanetakis.com/blog/using-curl-to-check-an-ssl-certificate-expiration-date-and-details

Рабочие методы в чистом Powershell: https://habr.com/ru/company/t1_cloud/blog/661107/
Ранее работавший метод: https://stackoverflow.com/a/20014855

Получить HTML-объекты определённого класса

$url = "https://jut.su/naruuto/season-2/episode-301.html"
# Нужно указать первый элемент [0], даже если он единственный.
(iwr $url).ParsedHtml.body.getElementsByClassName('header_video anime_padding_for_title_post_naruto')[0].innertext

https://stackoverflow.com/a/17625740

Информация

Вывод системной информации:

# Computer name
$comp = $env:COMPUTERNAME
# login
$login = $env:username
# Full user name
$fullusername = ([adsi]"WinNT://$env:userdomain/$env:username,user").fullname
# IP address
$ip = Get-NetIPAddress -AddressFamily IPv4 | where {$_.interfacealias -NotLike '*Loopback*' -AND $_.interfacealias -NotLike '*virtual*'} | select ipaddress -ExpandProperty ipaddress
# MAC
$mac = Get-NetAdapter -Physical | select macaddress -ExpandProperty macaddress
# IP и MAC для PoSH старых версий
# Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "IPEnabled = True" | where {$_.description -notlike '*virtual*'} |select IPAddress,MACAddress |fl @{n="IP"; e={$_.IPAddress}},@{n="MAC"; e={$_.MACAddress}}
# Time mark
$mark = get-date -UFormat "%d.%m.%Y %H:%M"
 
echo "Имя компьютера: $comp"
echo "Логин пользователя: $login"
echo "ФИО пользователя: $fullusername"
echo "IP: $ip"
echo "MAC: $mac"
echo "Время подготовки отчёта: $mark"

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

# Узнать удалённо, что стоит на server1
Invoke-Command -cn server1 -ScriptBlock {Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | select DisplayName,DisplayVersion,Publisher,InstallDate,UninstallString}
# Для 32-битных приложений на 64-битной ОС:
# HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*
 
# Вариант через Get-WmiObject (медленно)
gwmi win32_product -ComputerName server1 -Filter "Name like 'Microsoft Office%'"

https://devblogs.microsoft.com/scripting/use-powershell-to-find-installed-software/
https://social.technet.microsoft.com/Forums/en-US/dc42dd1d-737e-4f64-8fe2-e421f655836f/powershell-script-to-identify-office-version-on-a-remote-system-using-computer-name?forum=ITCG

Универсальный вариант: https://serverfault.com/questions/1111419/how-to-get-a-complete-list-of-all-installed-software-via-powershell

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

[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[System.Windows.Forms.MessageBox]::Show("Message Text","Title",1,48)

Первое число:
0 OK
1 OKCancel
2 AbortRetryIgnore
3 YesNoCancel
4 YesNo
5 RetryCancel

Второе число:
0 None
16 Hand
16 Error
16 Stop
32 Question
48 Exclamation
48 Warning
64 Asterisk
64 Information

https://michlstechblog.info/blog/powershell-show-a-messagebox/

Температура процессора

((gwmi -Namespace root\wmi MSAcpi_ThermalZoneTemperature).CurrentTemperature |sort |select -last 1) / 100

CMD:

@echo off
for /f "skip=1 tokens=2 delims==" %%A in ('wmic /namespace:\\root\wmi PATH MSAcpi_ThermalZoneTemperature get CurrentTemperature /value') do set /a "HunDegCel=(%%~A*10)-27315"
echo %HunDegCel:~0,-2%.%HunDegCel:~-2% Degrees Celsius
pause

Аптайм

read-host "Имя компа" |Set-Variable n
gcim win32_operatingsystem  -computername $n |
select Caption,Version,@{Name="Uptime";Expression={(Get-Date) - $_.lastBootUpTime}},PScomputername

Даты установки Windows

$report = gci -Path HKLM:\System\Setup\Source* |% {
Get-ItemProperty -Path Registry::$_} |
select ProductName, ReleaseID, CurrentBuild, 
@{n="InstallDate"; e={([DateTime]'1/1/1970').AddSeconds($_.InstallDate)}} |
sort {[int]($_.CurrentBuild)}
 
$report += gcim Win32_OperatingSystem |select @{n='ProductName';e={($_.Caption -replace 'майкрософт').Trim()}},ReleaseID,@{n='CurrentBuild';e={$_.BuildNumber}},InstallDate
 
$report
 
ProductName    ReleaseID CurrentBuild InstallDate
-----------    --------- ------------ -----------
Windows 10 Pro           10240        08.02.2016 7:39:56
Windows 10 Pro 1511      10586        08.02.2016 8:51:37
Windows 10 Pro 1607      14393        04.09.2016 14:17:50
Windows 10 Pro 1703      15063        10.07.2017 7:48:10
Windows 10 Pro 1709      16299        18.10.2017 19:55:47
Windows 10 Pro 1803      17134        04.05.2018 3:53:16
Windows 10 Pro 1809      17763        23.12.2018 20:11:00
Windows 10 Pro 1909      18363        06.12.2019 18:23:14
Windows 10 Pro 2009      19044        06.06.2020 6:43:13
Windows 11 Pro           22000        23.02.2022 12:42:49

Состояние диска

FIXME Добавить проверку и отправку писем, если статус не ОК

$i = gcim Win32_DiskDrive |? Partitions -gt 0 | select SystemName,Model,@{n='Size(GB)';e={$_.Size / 1GB -as [int]}},InterfaceType,Status,MediaType,SerialNumber
if ($i.Status -ne "OK") {Write-Host "Что-то с жёстким диском!";$i}

Total LBAs Written в мегабайты/гигабайты/терабайты

[single]$lbaWritten = Read-Host "Введите кол-во записанных LBA"
[single]$lbaSize = Read-Host "Введите размер блока, если он отличается от стандартного 512 КБ"
if (-not $lbaSize) {$lbaSize = 512}
$bytes = $lbaWritten * $lbaSize
 
Write-Host -fore Yellow "`nЗаписано на диск:"
"$($bytes / 1MB -as [int]) МБ"
"$($bytes / 1GB -as [int]) ГБ"
"$(($bytes / 1TB).tostring("0.00")) ТБ"

Сокращение десятичных знаков, округление

@{n='Size(MB)';e={($_.Length/1MB).ToString("#.##")}} # строка
[math]::round((0.4265867),2) # число
0,43

Внешний IP

curl https://2ip.ru

Поиск свободного IP в сети

1..254 |% {
$ip = "10.1.0.$_"
    if (-not (Test-Connection $ip -Count 1 -Quiet)) {
    $ip
    }
}

Потребление памяти несколькими одноимёнными процессами

get-process firefox |group name |select name,@{n='Memory(MB)';e={(($_.group.workingset |measure -sum).sum / 1mb).ToString('#.##')}}

Пути каталогов в профиле и системе

# Перечислить
[enum]::getnames([environment+specialfolder])
AdminTools
ApplicationData
CDBurning
CommonAdminTools
CommonApplicationData
CommonDesktopDirectory
CommonDocuments
CommonMusic
...
# Вывести путь
[environment]::getfolderpath('startup')
C:\Users\username\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup

Реестр

$regPath = "HKCU:\Software\Microsoft\Office\16.0\Outlook\Options\Mail"
New-ItemProperty -Path $regPath -Name JunkMailImportLists -PropertyType Dword -Value 1 -Force > $null

Список значений -PropertyType.

Binary		REG_BINARY
DWord		REG_DWORD
ExpandString	REG_EXPAND_SZ
MultiString	REG_MULTI_SZ
None		No data type
QWord		REG_QWORD
String		REG_SZ

https://learn.microsoft.com/en-us/dotnet/api/microsoft.win32.registryvaluekind

Прочее

Таймер

$timer = [system.diagnostics.stopwatch]::StartNew()
[math]::round(($timer.Elapsed.TotalSeconds),2)
$timer.IsRunning
True
$timer.Stop()

https://www.pluralsight.com/blog/tutorials/measure-powershell-scripts-speed

Секунды в dd.hh:mm:ss

[timespan]::fromseconds("336621").tostring()
3.21:30:21

Текст в речь

$currentdate = (get-date).ToLongDateString()
$currenttime = (get-date).ToLongTimeString()
Add-Type -AssemblyName System.speech
$speak = New-Object System.Speech.Synthesis.SpeechSynthesizer
$speak.Speak("Сегодня $currentdate, текущее время $currenttime")

https://learn-powershell.net/2013/12/04/give-powershell-a-voice-using-the-speechsynthesizer-class/

Скриншот

[void][reflection.assembly]::loadwithpartialname("system.windows.forms")
[system.windows.forms.sendkeys]::sendwait('{PRTSC}')
Get-Clipboard -Format Image | ForEach-Object -MemberName Save -ArgumentList "c:\temp\screenshot.png"

https://www.reddit.com/r/PowerShell/comments/9y9689/take_a_screenshot_with_powershell/

Установить/обновить Powershell 7

Windows:

iex "& { $(irm https://aka.ms/install-powershell.ps1) } -UseMSI"

Linux:

wget https://aka.ms/install-powershell.sh; sudo bash install-powershell.sh; rm install-powershell.sh

https://www.thomasmaurer.ch/2019/07/how-to-install-and-update-powershell-7/

Запуск скрипта Powershell из-под PSExec

@echo off
title Install Powershell 5.1
set /P COMP=computername:
psexec.exe \\%COMP% /s /h cmd /c powershell -executionpolicy bypass -file "\\domain.ru\share\Powershell\Win7-install-PS51.ps1"

Обработка объектов, перетащенных на ярлык

# Ярлык
powershell -f "C:\temp\drag-drop.ps1"
# вариант
powershell -noexit -noprofile -f "C:\temp\drag-drop.ps1"

При перетаскивании на ярлык в $args попадают полные пути объектов.

# Получить список файлов
$inputFiles = @()
$args |% {
    if ((get-item "$_").mode -match "^d") {
    $inputFiles += dir "$_" -File -Recurse
    }
    else {
    $inputFiles += get-item "$_"
    }
}

Out-Null

Никогда не нужно пользоваться командлетом Out-Null, он тормозной. Вместо него (по убыванию быстродействия)

$array = New-Object -TypeName System.Collections.ArrayList
 
# $null (assigned to $null) - RECOMMENDED
$null = $array.Add('A')
 
# [void] (cast to void) - RECOMMENDED
[void]$array.Add('B')
 
# $null (redirected to $null)
$array.Add('C') > $null 

https://powershell-guru.com/powershell-best-practice-12-avoid-out-null/

Путь к файлу процесса на удалённой машине

Get-Process не умеет получать путь с удалённой машины. Нужно через WMI:

Get-WmiObject -Class win32_process -ComputerName $MyServer -Filter 'name like "%iexplore%"' | select processname,path

https://stackoverflow.com/questions/49348230/finding-path-of-process-on-remote-machine

Работа с PDF

https://www.mikesdotnetting.com/article/80/create-pdfs-in-asp-net-getting-started-with-itextsharp

https://www.timatlee.com/post/powershell-itext/