# 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]}
===== Таблички, отчёты, массивы =====
$arrayList = [System.Collections.ArrayList]::new()
$arrayList.add("aaa") > $null
''[System.Collections.Generic.List[]]'' - [[https://learn.microsoft.com/ru-ru/powershell/scripting/learn/deep-dives/everything-about-arrays?view=powershell-7.4#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 для фонового цвета перемежающихся строк
* #EDEDED - очень светлый серый.
* #D9D9D9 - серый чуть потемнее.
* #DBDBDB - серый. У Экселя есть шаблон, где он перемежается с #EDEDED.
* #A5A5A5 - серый темнее. Лучше пользоваться уже белым шрифтом.
* #7B7B7B - тёмно-серый. У Экселя есть шаблон, где он перемежается с #A5A5A5.
==== Массив русского алфавита с индексом ====
Почему не ''[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: [[https://www.mynikko.com/dummy/|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"
[[https://www.serverbrain.org/system-administration/reading-large-text-files-with-powershell.html|Reading large text files with Powershell]]\\
[[https://stackoverflow.com/questions/55468899/speed-issue-on-match-operation-for-large-text-file|Speed issue on -match operation for large text file]]\\
[[https://learn.microsoft.com/en-us/powershell/scripting/dev-cross-plat/performance/script-authoring-considerations?view=powershell-7.3|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) {
'(?
==== Список модулей 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")
{{:scripts:pasted:20200813-150710.png?450}}{{:scripts:pasted:20200814-150314.png?600}}
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 "Hello
" -BodyAsHtml -Encoding UTF8 -UseSsl
==== Письмо с картинкой в тексте ====
Send-MailMessage -To "testovtt@domain.ru" -From "birthday@domain.ru" `
-SmtpServer "mail.domain.ru" -Subject "С днём рожденья!" `
-Body "
Уважаемый Тест Тестович!
Поздравляю вас с днём рождения!
С уважением, скрипт.
" -Attachments "C:\scripts\birthday.jpg" -BodyAsHtml -Encoding utf8
https://stackoverflow.com/questions/52837468/embed-images-in-email-and-send-via-powershell
==== Сколько дней осталось до окончания действия SSL-сертификата ====
С помощью curl (надо [[https://curl.se/windows/|скачивать с сайта]], встроенный в винду многого не умеет)
$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/