Od „klikania” do automatyzacji – co chcesz zautomatyzować?
Masz poczucie, że ciągle robisz w Windows te same kroki: otwierasz Eksplorator, kopiujesz pliki, sprawdzasz logi, odpalasz kilka tych samych aplikacji? To jest dokładnie ten moment, w którym PowerShell zaczyna się opłacać. Zanim jednak zaczniesz pisać skrypty, dobrze jest nazwać swoje cele: co dokładnie chcesz, żeby działo się „samo”?
Diagnoza: które ręczne czynności najbardziej cię męczą?
Zacznij od listy trzech–pięciu działań, które powtarzają się kilka razy dziennie lub kilka razy w tygodniu. Zadaj sobie kilka prostych pytań:
- Czy często kopiujesz lub przenosisz pliki między tymi samymi katalogami lub serwerami?
- Czy ręcznie tworzysz raporty: np. listę użytkowników, wolne miejsce na dyskach, stany usług?
- Czy regularnie sprzątasz katalogi z logami, backupami, plikami tymczasowymi?
- Czy konfigurujesz nowe komputery w podobny sposób: instalujesz te same aplikacje, ustawiasz te same opcje?
- Czy odpalasz serię narzędzi diagnostycznych i zapisujesz ich wyniki w plikach?
Na które z tych pytań odpowiadasz „tak”? Właśnie tam jest największy potencjał automatyzacji. Zamiast myśleć „napiszę jakiś skrypt w PowerShell”, lepiej postawić bardziej precyzyjny cel: „Chcę, żeby po podłączeniu nowego komputera jeden skrypt skonfigurował system, zainstalował aplikacje i zapisał log z przebiegu”.
Jednorazowy skrypt a narzędzie dla zespołu
Inaczej pisze się skrypt „dla siebie na dziś”, a inaczej narzędzie, z którego będzie korzystać zespół przez lata. Zanim otworzysz edytor, odpowiedz sobie na dwie rzeczy:
- Kto będzie używał skryptu? Tylko ty, czy również inni administratorzy, helpdesk, dział biznesowy?
- Jak długo skrypt ma żyć? Jednorazowe zadanie, czy element stałego procesu (np. onboarding użytkownika)?
Jeśli skrypt jest jednorazowy, możesz zaakceptować prostszy kod, mniej parametrów, minimalną obsługę błędów. Ale jeśli ma to być narzędzie dla zespołu, potrzebujesz:
- czytelnych komunikatów, nie tylko dla autora,
- konfigurowalnych parametrów (np. ścieżki, tryby „na sucho”),
- logowania działań do pliku,
- obsługi błędów, która nie zatrzymuje wszystkiego po pierwszej przeszkodzie.
Jak oceniasz swój aktualny skrypt: wygląda bardziej jak „polecenia w pliku”, czy jak małe narzędzie? To ważne, bo wpływa na sposób projektowania struktury.
Jak przełożyć „ciągle robię X” na zadanie skryptowe
Każdą manualną czynność w Windows można opisać trzema prostymi elementami: wejście, logika, wyjście. Bez tego skrypt staje się zbiorem przypadkowych komend.
- Wejście – skąd skrypt wie, na czym ma zadziałać?
- Ścieżki do katalogów, nazwy serwerów, lista użytkowników?
- Dane z pliku CSV/JSON, parametrów wiersza poleceń, zmiennych środowiskowych?
- Logika – jakie decyzje skrypt ma podejmować?
- Jakie pliki kopiować (po rozszerzeniu, dacie, rozmiarze)?
- Co zrobić, gdy plik już istnieje? Nadpisać, pominąć, dodać sufiks?
- Jak reagować na brak uprawnień – przerwać pracę czy kontynuować?
- Wyjście – jaki rezultat chcesz widzieć?
- Pliki w nowym miejscu, raport CSV, wpisy w logu?
- Wynik na ekranie, który ktoś odczyta, czy dane dla kolejnego skryptu?
Spróbuj rozpisać swoją czynność na trzy–cztery zdania w tym formacie. Na przykład: „Wejście: lista serwerów z pliku CSV. Logika: połącz się, sprawdź miejsca na dyskach, oznacz alert, jeśli jest mniej niż 10%. Wyjście: plik CSV z kolumnami Serwer, Dysk, Zajętość, Alert”. To już jest szkielet pod realny skrypt PowerShell.
Pierwsze 2–3 mini-projekty, które szybko dają efekt
Zamiast rzucać się od razu na ogromne wdrożenie, zacznij od małych rzeczy, które dają natychmiastową oszczędność czasu i pokazują „magię” PowerShell. Co może być takim projektem?
- Automatyczne sprzątanie katalogu „Pobrane” – usuwanie plików starszych niż 30 dni, przenoszenie dużych plików do archiwum.
- Codzienny raport wolnego miejsca na dyskach – wysyłany mailem lub zapisywany do pliku sieciowego.
- Masowe zakładanie kont użytkowników na bazie pliku CSV (AD lub lokalnie, w zależności od środowiska).
Wybierz coś, co:
(a) wykonujesz regularnie,
(b) da się łatwo zmierzyć (czas przed/po),
(c) nie niesie ogromnego ryzyka (lepiej raport niż hurtowe kasowanie plików na początek).
Jakie trzy zadania przychodzą ci do głowy jako pierwsze? Zrób ich listę, obok dopisz „wejście, logika, wyjście”. To będzie twoja mapa do nauki PowerShell.

Podstawy PowerShell, które naprawdę musisz opanować
Konsola, ISE, VS Code – gdzie wygodnie pisać skrypty?
PowerShell to zarówno powłoka interaktywna, jak i język skryptowy. Możesz go używać jak „lepszego CMD” albo jak pełnoprawnego środowiska do tworzenia narzędzi. Gdzie to robić w praktyce?
Masz trzy najczęstsze warianty:
- Windows PowerShell / PowerShell (pwsh) – konsola
- Idealna do szybkiego testowania pojedynczych komend i fragmentów skryptu.
- Przydaje się do pracy interaktywnej na serwerach (np. przez RDP, SSH).
- Windows PowerShell ISE
- Stare, ale nadal spotykane środowisko z podglądem konsoli i edytorem.
- Dobre do nauki, ale rozwój zatrzymał się – lepiej stopniowo przesiąść się na VS Code.
- Visual Studio Code z rozszerzeniem PowerShell
- Kolorowanie składni, IntelliSense, debuger, integracja z Git.
- Najlepszy wybór, jeśli myślisz o poważniejszych skryptach i modułach.
Jak teraz testujesz polecenia PowerShell – w konsoli, w ISE, w notatniku? Dobrym nawykiem jest: polecenie testujesz w konsoli, a to, co ma wracać potem, dopisujesz do pliku .ps1 w edytorze. Dzięki temu unikasz sytuacji, w której jedyny działający kod żyje w historii konsoli.
PowerShell vs CMD – dlaczego „wszystko jest obiektem”
Przesiadka z CMD lub batch na PowerShell jest jak przejście z roweru na samochód. W Batch wszystko jest tekstem, a w PowerShell prawie wszystko jest obiektem .NET. Co to znaczy w praktyce?
- Polecenie
Get-Processzwraca obiekty procesów z właściwościami (Name, Id, CPU, WorkingSet), a nie tekstowe linijki. - Polecenie
Get-ChildItem(aliasdir) zwraca obiekty plików i katalogów z właściwościami (Name, Length, LastWriteTime, Attributes). - Konwersja do i z formatów jak CSV, JSON dzieje się na poziomie obiektów, a nie ręcznego sklejania tekstu.
W efekcie możesz pisać polecenia w stylu:
Get-Process | Where-Object CPU -gt 50 | Sort-Object CPU -Descending | Select-Object -First 5
i działa to tak, jakbyś filtrował i sortował w Excelu – ale w konsoli. Zadaj sobie pytanie: czy częściej myślisz o „linijkach tekstu”, czy o „rzeczach z właściwościami”? Im szybciej zaczniesz patrzeć na dane jak na obiekty, tym łatwiej będzie budować wydajne skrypty.
Podstawowe „kompasy”: Get-Help, Get-Command, Get-Member
PowerShell ma ogromnie rozbudowaną pomoc wbudowaną, którą mało kto wykorzystuje w pełni. Trzy absolutnie kluczowe komendy to:
- Get-Help – dokumentacja cmdletów, funkcji, skryptów.
- Get-Command – wyszukiwanie poleceń po nazwie, module, typie.
- Get-Member – inspekcja obiektów, właściwości i metod.
Przykłady użycia:
# Szukanie poleceń dotyczących usług
Get-Command *service*
# Pomoc do konkretnego polecenia
Get-Help Get-Service -Detailed
# Pobranie właściwości zwracanych przez Get-Process
Get-Process | Get-Member
Zadaj sobie pytanie: ile razy w tygodniu wpisujesz Get-Help? Jeśli odpowiedź brzmi „prawie nigdy”, prawdopodobnie spędzasz dużo więcej czasu na Google niż musisz. Dobrym nawykiem jest zawsze zajrzeć do Get-Help, gdy używasz cmdleta po raz pierwszy.
Konfiguracja środowiska: uprawnienia i ExecutionPolicy
Pierwsze zderzenie ze skryptami PowerShell często wygląda tak: zapisujesz plik .ps1, uruchamiasz go i widzisz błąd o ExecutionPolicy. Dlaczego tak się dzieje?
PowerShell ma mechanizm Execution Policy, który kontroluje, jakie skrypty mogą się uruchamiać. Podstawowe poziomy to m.in.:
- Restricted – żadnych skryptów, tylko interaktywne komendy.
- RemoteSigned – lokalne skrypty mogą działać, zdalne muszą mieć podpis.
- Unrestricted – wszystkie skrypty mogą działać (z ostrzeżeniami).
Sprawdzasz je poleceniem:
Get-ExecutionPolicy -List
A zmieniasz (np. dla bieżącego użytkownika) w taki sposób:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Zadaj sobie pytanie: na jakim poziomie bezpieczeństwa ci zależy? W środowisku produkcyjnym lepiej trzymać się RemoteSigned albo rozwiązań z podpisywaniem skryptów certyfikatem, zamiast bezrefleksyjnie ustawiać Unrestricted dla całego systemu.
Pierwszy skrypt .ps1 – od pliku do uruchomienia
Najprostsza droga od „komendy w konsoli” do „skryptu, który coś robi” wygląda tak:
- Wybierz katalog, gdzie trzymasz skrypty, np.
C:Scripts. - Utwórz plik
Przyklad.ps1w edytorze (VS Code, ISE, Notepad++). - Wpisz kilka komend, np.:
Write-Host "Start sprzątania..."
Get-ChildItem "C:Temp" -Recurse -File |
Where-Object LastWriteTime -lt (Get-Date).AddDays(-7) |
Remove-Item -WhatIf
Write-Host "Koniec."
- Zapisz plik, przejdź w konsoli do katalogu skryptu:
Set-Location C:Scripts
.Przyklad.ps1
Jeśli ExecutionPolicy przeszkadza, dostosuj go dla bieżącego użytkownika. Zastanów się: jakie trzy komendy, które często wpisujesz w konsoli, możesz po prostu wrzucić do pliku .ps1 i uruchamiać jednym poleceniem?
Składnia, zmienne i potoki – jak „myśli” PowerShell
Zmienne i typy obiektów w codziennej administracji
Bez zmiennych nie ma sensownego skryptu. W PowerShell każda zmienna zaczyna się od $ i może przechowywać właściwie wszystko: liczby, tekst, obiekty, tablice, hashtablice.
Kilka praktycznych przykładów:
# Zmienna tekstowa
$UserName = "Jan Kowalski"
# Liczba (np. próg użycia dysku)
$DiskThreshold = 80
# Obiekt (np. pojedynczy proces)
$Proc = Get-Process -Name "explorer"
# Tablica obiektów (np. procesy przeglądarki)
$Browsers = Get-Process -Name "chrome", "msedge"
# Hashtabela (np. konfiguracja skryptu)
$Config = @{
SourcePath = "C:Dane"
BackupPath = "serwerbackup"
DaysToKeep = 30
}
Pytanie pomocnicze: jak zazwyczaj „trzymasz” dane w swoich skryptach – w kilku oddzielnych zmiennych, czy już używasz hashtabel i obiektów? Przestawienie się na obiektowe podejście pozwala łatwo przekazywać konfigurację do funkcji i uniknąć dziesiątek parametrów.
Operatory: porównania, logika i arytmetyka na przykładach
PowerShell ma dość czytelne operatory porównania. Najczęściej używane to:
Operatory w praktyce: jak decydować w skrypcie
Bez sensownych porównań skrypt nie podejmuje decyzji, tylko „wypluwa” dane. Operatory w PowerShell mają czytelne nazwy, ale szybko robi się gęsto, jeśli używasz ich bez planu. Jakim warunkiem najczęściej sterujesz swoje skrypty – czasem, nazwą, progiem procentowym?
Najczęściej używane operatory porównania:
-eq– równe-ne– różne-gt,-ge– większe, większe lub równe-lt,-le– mniejsze, mniejsze lub równe-like– dopasowanie z*(wildcard)-match– dopasowanie regex
Przykłady z życia admina:
# Sprawdzenie progu użycia CPU
if ($CpuUsage -gt 80) {
Write-Host "CPU powyżej 80% – czas sprawdzić procesy." -ForegroundColor Yellow
}
# Dopasowanie nazwy pliku logu
if ($LogFileName -like "*Error*.log") {
Write-Host "To jest plik z błędami: $LogFileName"
}
# Szukanie użytkowników z określonym prefiksem
Get-LocalUser | Where-Object Name -like "serwis_*"
Przy operatorach logicznych (-and, -or, -not) dobrze zadać sobie pytanie: „czy ten warunek naprawdę chcę uruchamiać zawsze?”. W jednym z typowych skryptów porządkowych sprawdzasz na przykład i wiek pliku, i rozszerzenie:
$Days = 30
Get-ChildItem "C:Logi" -File -Recurse |
Where-Object {
$_.Extension -eq ".log" -and
$_.LastWriteTime -lt (Get-Date).AddDays(-$Days)
} |
Remove-Item -WhatIf
Jeżeli warunek zaczyna być długi i nieczytelny, rozbij go na kilka kroków:
$OldDate = (Get-Date).AddDays(-$Days)
$Files = Get-ChildItem "C:Logi" -File -Recurse
$OldLogs = $Files | Where-Object {
$_.Extension -eq ".log" -and $_.LastWriteTime -lt $OldDate
}
$OldLogs | Remove-Item -WhatIf
Co jest teraz czytelniejsze dla ciebie za tydzień – jedno długie Where-Object, czy kilka nazwanych kroków?
Potok: przekazywanie obiektów krok po kroku
Potok (|) to serce PowerShell. Nie chodzi tylko o łączenie poleceń, ale o budowanie „taśmy produkcyjnej” dla obiektów. Jak zwykle filtrujesz dane – od razu w jednym cmdlecie, czy krokami?
Ogólny wzór, który dobrze sobie utrwalić, to:
Źródło | Filtrowanie | Sortowanie | Selekcja | Format / Eksport
Na przykład – raport o procesach zużywających najwięcej pamięci:
Get-Process |
Where-Object WorkingSet -gt 100MB |
Sort-Object WorkingSet -Descending |
Select-Object -First 10 Name, Id, CPU,
@{ Name = "MemoryMB"; Expression = { [math]::Round($_.WorkingSet / 1MB, 1) } } |
Format-Table -AutoSize
Każdy z tych kroków możesz osobno testować w konsoli. Najpierw Get-Process. Potem dodajesz | Where-Object .... Dopiero na końcu kombinujesz z wyliczeniami w Select-Object. Zapytaj siebie: jak często debugujesz całą linię 200-znakową, zamiast po prostu uruchomić pierwszy fragment potoku?
Drugi typ potoku, który często się przydaje, to przekazywanie do cmdletów typu Set-* lub Remove-*:
# Zatrzymanie wszystkich usług, które zaczynają się od "Test"
Get-Service "Test*" | Where-Object Status -eq "Running" | Stop-Service -WhatIf
# Usunięcie użytkowników lokalnych, których konta są zablokowane
Get-LocalUser | Where-Object Enabled -eq $false | Remove-LocalUser -WhatIf
Pytanie kontrolne: czy przed operacją modyfikującą (Stop, Remove, Set) zawsze uruchamiasz tę samą komendę bez ostatniego cmdleta, żeby zobaczyć, co trafi na wejście?
Instrukcje warunkowe i pętle – sterowanie przepływem
PowerShell używa klasycznych konstrukcji: if, switch, foreach, for, while, ale w administracji najczęściej wystarczą trzy: if, foreach i switch.
Typowy warunek z „gałęziami”:
if ($FreeSpacePercent -lt 10) {
Write-Host "KRYTYCZNIE mało miejsca!" -ForegroundColor Red
}
elseif ($FreeSpacePercent -lt 20) {
Write-Host "Mało miejsca, trzeba posprzątać." -ForegroundColor Yellow
}
else {
Write-Host "Miejsca jest w miarę spokojnie." -ForegroundColor Green
}
Iteracja po kolekcji obiektów:
$Servers = @("SRV-FS01", "SRV-FS02", "SRV-APP01")
foreach ($Server in $Servers) {
Write-Host "Sprawdzam usługi na serwerze $Server..."
Get-Service -ComputerName $Server |
Where-Object Status -eq "Stopped" |
Select-Object -First 5 Name, Status
}
Jeżeli masz wiele wariantów tej samej zmiennej (np. tryb pracy skryptu, poziom logowania), wygodniej użyć switch:
$Mode = "Test"
switch ($Mode) {
"Test" {
$WhatIf = $true
$LogLevel = "Verbose"
}
"Prod" {
$WhatIf = $false
$LogLevel = "Info"
}
default {
throw "Nieznany tryb: $Mode (użyj Test lub Prod)"
}
}
Jak teraz wybierasz zachowanie skryptu – zmieniasz ręcznie linijki w środku, czy sterujesz jednym parametrem/zmienną na początku?

Struktura skryptu, funkcje i parametry – od „pliku z komendami” do narzędzia
Minimalna „higiena” pliku .ps1
Skrypt, który da się utrzymać, zwykle ma przejrzystą strukturę. Wystarczy kilka sekcji:
- nagłówek (komentarze, opis, autor, wersja),
- blok parametrów (
param()), - konfiguracja (stałe, ścieżki domyślne),
- funkcje pomocnicze,
- część główna (logika wykonania).
Prosty przykład takiego „szkieletu”:
<#
.SYNOPSIS
Czyści stare pliki z katalogu tymczasowego.
.DESCRIPTION
Usuwa pliki starsze niż podana liczba dni, z opcją trybu testowego.
#>
param(
[Parameter(Mandatory)]
[string]$Path,
[int]$DaysToKeep = 14,
[switch]$WhatIf
)
# Konfiguracja
$LimitDate = (Get-Date).AddDays(-$DaysToKeep)
function Write-Log {
param(
[string]$Message,
[string]$Level = "INFO"
)
$time = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Write-Host "[$time][$Level] $Message"
}
# Część główna
Write-Log "Start czyszczenia katalogu $Path (zostawiamy ostatnie $DaysToKeep dni)."
$FilesToDelete = Get-ChildItem $Path -File -Recurse |
Where-Object LastWriteTime -lt $LimitDate
Write-Log "Znaleziono $($FilesToDelete.Count) plików do usunięcia."
if ($WhatIf) {
Write-Log "Tryb testowy – żadnych plików nie usuwam." "WARN"
$FilesToDelete | Select-Object FullName, LastWriteTime
}
else {
$FilesToDelete | Remove-Item -Force
Write-Log "Usuwanie zakończone."
}
Jak wygląda typowy plik .ps1 u ciebie – widać od razu, gdzie są parametry, a gdzie część główna, czy wszystko jest „wymieszane” od góry do dołu?
Tworzenie funkcji: zamiana powtarzających się bloków w narzędzia
Funkcja w PowerShell to tak naprawdę „mini-cmdlet” wewnątrz twojego skryptu. Za każdym razem, gdy kopiujesz ten sam blok kodu w kilku miejscach, zatrzymaj się i zadaj pytanie: czy nie lepiej zrobić z tego funkcji?
function Get-OldFiles {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$Path,
[int]$OlderThanDays = 30
)
$limit = (Get-Date).AddDays(-$OlderThanDays)
Get-ChildItem $Path -File -Recurse |
Where-Object LastWriteTime -lt $limit
}
Taka funkcja może być użyta w kilku różnych skryptach: jednym do usuwania plików, drugim do archiwizacji, trzecim do raportowania.
# Archiwizacja starych plików
$OldFiles = Get-OldFiles -Path "C:Dane" -OlderThanDays 60
$OldFiles | Compress-Archive -DestinationPath "C:ArchiwumStare.zip"
# Raport starych plików
Get-OldFiles -Path "D:Logi" -OlderThanDays 90 |
Select-Object FullName, LastWriteTime |
Export-Csv "D:RaportyStarePliki.csv" -NoTypeInformation -Delimiter ";"
Co możesz wyodrębnić dziś z jednego dłuższego skryptu do osobnej funkcji, żeby używać tego jutro w innym zadaniu?
Parametry: jak „sterować” zachowaniem skryptu bez edycji kodu
Jeżeli za każdym razem przed uruchomieniem skryptu edytujesz ręcznie ścieżkę, zakres dat albo listę serwerów, to znak, że brakuje ci parametrów. Parametry to kontrakt między tobą (autorem) a użytkownikiem (często też tobą za pół roku).
Przykładowy blok parametrów do skryptu raportującego dyski:
param(
[Parameter(Mandatory)]
[string[]]$ComputerName,
[int]$ThresholdPercent = 20,
[string]$ReportPath = "C:RaportyDyski.csv"
)
$results = foreach ($Computer in $ComputerName) {
Get-CimInstance -ClassName Win32_LogicalDisk -ComputerName $Computer -Filter "DriveType = 3" |
Select-Object @{Name="ComputerName"; Expression={ $Computer }},
DeviceID,
@{Name="SizeGB"; Expression={ [math]::Round($_.Size / 1GB, 1) }},
@{Name="FreeGB"; Expression={ [math]::Round($_.FreeSpace / 1GB, 1) }},
@{Name="FreePercent"; Expression={ [math]::Round(($_.FreeSpace / $_.Size) * 100, 0) }}
}
$results |
Where-Object FreePercent -lt $ThresholdPercent |
Export-Csv $ReportPath -NoTypeInformation -Delimiter ";"
Uruchomienie może wyglądać tak:
.RaportDyski.ps1 -ComputerName "SRV-FS01","SRV-FS02" -ThresholdPercent 15
Pełną moc daje dopiero użycie atrybutów parametrów: [Parameter(Mandatory)], ValidateSet, ValidateRange itp.
param(
[Parameter(Mandatory)]
[ValidateScript({ Test-Path $_ })]
[string]$Path,
[ValidateRange(1,365)]
[int]$DaysToKeep = 30,
[ValidateSet("Info","Debug","Verbose")]
[string]$LogLevel = "Info"
)
Pomyśl, jaki błąd użytkownika chcesz „złapać” jak najwcześniej – nieistniejący katalog, zakres dni, tryb pracy? To idealne miejsce na walidację parametrów.
Advanced Functions: cmdlety własnej roboty
Jeśli często korzystasz z potoku i chcesz, by twoje funkcje zachowywały się jak wbudowane cmdlety, zmień je w tzw. „advanced functions”. W praktyce sprowadza się to do użycia [CmdletBinding()] i atrybutów parametrów.
function Get-DiskReport {
[CmdletBinding()]
param(
[Parameter(
Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName
)]
[Alias("Name","Computer")]
[string[]]$ComputerName
)
begin {
$allResults = @()
}
process {
foreach ($Computer in $ComputerName) {
Write-Verbose "Sprawdzam dyski na $Computer..."
$results = Get-CimInstance -ClassName Win32_LogicalDisk -ComputerName $Computer -Filter "DriveType = 3" |
Select-Object @{Name="ComputerName"; Expression={ $Computer }},
DeviceID,
@{Name="SizeGB"; Expression={ [math]::Round($_.Size / 1GB, 1) }},
@{Name="FreeGB"; Expression={ [math]::Round($_.FreeSpace / 1GB, 1) }},
@{Name="FreePercent"; Expression={ [math]::Round(($_.FreeSpace / $_.Size) * 100, 0) }}
$allResults += $results
}
}
end {
$allResults
}
}
Taka funkcja przyjmuje dane z potoku i może być użyta tak:
"SRV-FS01","SRV-FS02" | Get-DiskReport | Where-Object FreePercent -lt 15
Jeżeli wiesz, że daną funkcję będziesz potem wielokrotnie łączyć z innymi cmdletami, spróbuj od razu napisać ją jako advanced function. Zadaj sobie pytanie: czy tę funkcję chcę wywoływać tylko bezpośrednio, czy także w złożonych potokach?
Moduły: pakowanie funkcji w biblioteki
W pewnym momencie liczba skryptów i funkcji zaczyna rosnąć i ciężko tym zarządzać. Zamiast mieć 20 plików .ps1 kopiowanych po różnych serwerach, lepiej zbudować moduł.
Najprostsze podejście:
- Tworzysz folder modułu, np.
C:UsersTwojUserDocumentsWindowsPowerShellModulesAdminTools.
Moduły: jak z 20 skryptów zrobić jedno narzędzie
- W tym folderze tworzysz plik
AdminTools.psm1. - Przenosisz do niego funkcje, które chcesz współdzielić między skryptami.
- W osobnym pliku
AdminTools.psd1(manifest) możesz opisać wersję, autora, wymagania itd. – na początek nie jest to obowiązkowe.
Przykładowy, bardzo prosty moduł:
# C:UsersTwojUserDocumentsWindowsPowerShellModulesAdminToolsAdminTools.psm1
function Get-OldFiles {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$Path,
[int]$OlderThanDays = 30
)
$limit = (Get-Date).AddDays(-$OlderThanDays)
Get-ChildItem $Path -File -Recurse |
Where-Object LastWriteTime -lt $limit
}
function Get-DiskReport {
[CmdletBinding()]
param(
[Parameter(
Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName
)]
[Alias("Name","Computer")]
[string[]]$ComputerName
)
process {
foreach ($Computer in $ComputerName) {
Get-CimInstance -ClassName Win32_LogicalDisk -ComputerName $Computer -Filter "DriveType = 3" |
Select-Object @{Name="ComputerName"; Expression={ $Computer }},
DeviceID,
@{Name="SizeGB"; Expression={ [math]::Round($_.Size / 1GB, 1) }},
@{Name="FreeGB"; Expression={ [math]::Round($_.FreeSpace / 1GB, 1) }},
@{Name="FreePercent"; Expression={ [math]::Round(($_.FreeSpace / $_.Size) * 100, 0) }}
}
}
}
Export-ModuleMember -Function Get-OldFiles, Get-DiskReport
Po zapisaniu takiego modułu w katalogu modułów użytkownika możesz go załadować jednym poleceniem:
Import-Module AdminTools
Get-Module AdminTools
Od tej pory funkcje z modułu są dostępne w każdej sesji, w której go zaimportujesz. Zastanów się, których funkcji używasz „wszędzie” – logowanie, walidacja, praca z dyskami, może operacje na AD?
Im szybciej zaczniesz pakować powtarzalny kod w moduły, tym mniej będziesz mieć „kopiuj-wklej” po serwerach i tym łatwiej wprowadzisz jedną poprawkę w jednym miejscu.
Konwencje nazewnicze i styl: jak pisać skrypty, które rozumiesz po roku
Automatyzacja to nie tylko „działający” kod. To też kod, który umiesz szybko przeczytać, kiedy dzwoni ktoś o 2 w nocy. Jak nazywasz funkcje i zmienne – losowo, czy zgodnie z jakimś schematem?
Kilka praktycznych zasad:
- Funkcje – trzymaj się schematu
Czasownik-Rzeczownik(tak jak wbudowane cmdlety):Get-UserReport,Set-LogSettings,Remove-OldBackups. - Zmienne – nazwy opisowe, ale nie przesadnie długie:
$Servers,$LimitDate,$LogFilePath. - Stałe / konfiguracja – wielkimi literami albo z prefiksem, np.
$Global:DefaultLogPath,$SCRIPT:BackupRoot. - Pliki – w nazwie cel i obszar:
Backup-SQLDatabases.ps1,Report-LocalAdmins.ps1,Cleanup-TempFiles.ps1.
Jeśli dziś tworzysz nowy skrypt, zapytaj siebie: „Jak nazwę to narzędzie, żeby po nazwie było jasne, co robi?”. Wyobraź sobie, że ma się znaleźć w katalogu z 50 innymi .ps1.
Drobny przykład różnicy w czytelności:
# Słabo
$a = Get-ChildItem $p -Recurse | ? { $_.Length -gt 100MB }
# Lepiej
$FilesLargerThan100MB = Get-ChildItem -Path $Path -Recurse |
Where-Object Length -gt 100MB
Którą wersję wolałbyś zobaczyć, gdy szukasz przyczyny pełnego dysku?
Praca z systemem Windows: pliki, rejestr, usługi, procesy
Pliki i foldery: od Get-ChildItem do masowych operacji
Zarządzanie plikami to chyba najczęstszy temat pierwszych skryptów. Pytanie, które warto sobie zadać: pracujesz na pojedynczych ścieżkach, czy myślisz od razu w kategoriach setek plików w pętli lub potoku?
Podstawowy zestaw poleceń:
Get-ChildItem– pobieranie listy plików i folderów,Copy-Item,Move-Item,Remove-Item– kopiowanie, przenoszenie, usuwanie,Test-Path– sprawdzanie istnienia pliku/katalogu,New-Item– tworzenie folderów, plików (np. pustych logów).
Przykład małego, ale użytecznego skryptu: przenoszenie logów starszych niż X dni do archiwum.
param(
[Parameter(Mandatory)]
[ValidateScript({ Test-Path $_ })]
[string]$SourcePath,
[Parameter(Mandatory)]
[ValidateScript({ Test-Path $_ })]
[string]$ArchivePath,
[ValidateRange(1,365)]
[int]$OlderThanDays = 30
)
$limit = (Get-Date).AddDays(-$OlderThanDays)
Get-ChildItem -Path $SourcePath -File -Recurse |
Where-Object LastWriteTime -lt $limit |
ForEach-Object {
$target = Join-Path $ArchivePath $_.Name
Move-Item -Path $_.FullName -Destination $target
}
Użycie potoku zamiast ręcznych pętli ułatwia później wstawienie dodatkowych kroków – filtrowanie po rozszerzeniu, dodatkowe logowanie, kompresję.
Jeżeli pracujesz z danymi tekstowymi (konfiguracje, wyniki poleceń), kluczowe są:
Get-Content,Set-Content,Add-Content,ConvertFrom-Json,ConvertTo-Json,Import-Csv,Export-Csv.
Przerobienie „ręcznego” otwierania pliku w Notatniku na prosty raport CSV często zajmuje mniej niż godzinę, a ratuje czas przez kolejne miesiące.
Rejestr Windows: konfiguracja pod kontrolą
Rejestr to miejsce, którego wielu adminów unika w skryptach. A można nim sterować w dość komfortowy sposób. Co aktualnie robisz ręcznie w regedit, co dałoby się powtarzalnie zautomatyzować?
Najważniejsze cmdlety:
Get-Item,Get-ItemProperty– odczyt kluczy i wartości,New-Item,New-ItemProperty– tworzenie kluczy i wartości,Set-ItemProperty– zmiana wartości,Remove-Item,Remove-ItemProperty– usuwanie.
Prosty przykład: wymuszenie konkretnej strony domowej w Edge dla wszystkich użytkowników na danej maszynie:
$RegPath = "HKLM:SOFTWAREPoliciesMicrosoftEdge"
$ValueName = "HomepageLocation"
$Url = "https://intranet.firma.local"
if (-not (Test-Path $RegPath)) {
New-Item -Path $RegPath -Force | Out-Null
}
$existing = Get-ItemProperty -Path $RegPath -Name $ValueName -ErrorAction SilentlyContinue
if ($null -eq $existing) {
New-ItemProperty -Path $RegPath -Name $ValueName -Value $Url -PropertyType String -Force | Out-Null
}
else {
Set-ItemProperty -Path $RegPath -Name $ValueName -Value $Url
}
Dzięki takiemu podejściu masz prostą idempotencję: możesz uruchamiać skrypt wielokrotnie, a on tylko utrwala pożądaną konfigurację. Jakie ustawienie w rejestrze ustawiasz dziś ręcznie na kilku komputerach?
Usługi: start, stop, konfiguracja na wielu serwerach
Kontrola usług to klasyka automatyzacji. Zamiast logować się na każdy serwer po kolei i klikać w services.msc, zbierz serwery w listę i zrób to w jednym przebiegu. Co częściej robisz: restartujesz usługę, zmieniasz typ uruchomienia, czy monitorujesz stan?
Podstawowy zestaw:
Get-Service– pobieranie informacji o usługach,Start-Service,Stop-Service,Restart-Service,Set-Service– zmiana opisu, konta, trybu uruchomienia.
Przykład: masowy restart konkretnej usługi na liście serwerów, z logowaniem wyniku:
param(
[Parameter(Mandatory)]
[string[]]$ComputerName,
[Parameter(Mandatory)]
[string]$ServiceName,
[string]$LogPath = "C:RaportyRestartUslug.log"
)
function Write-Log {
param(
[string]$Message
)
$line = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') `t $Message"
Add-Content -Path $LogPath -Value $line
}
foreach ($Server in $ComputerName) {
try {
Write-Log "Sprawdzam usługę $ServiceName na $Server..."
$svc = Get-Service -ComputerName $Server -Name $ServiceName -ErrorAction Stop
if ($svc.Status -ne "Running") {
Write-Log "Usługa $ServiceName nie działa na $Server – uruchamiam."
Start-Service -InputObject $svc
}
else {
Write-Log "Restartuję usługę $ServiceName na $Server."
Restart-Service -InputObject $svc
}
}
catch {
Write-Log "BŁĄD na $Server: $($_.Exception.Message)"
}
}
Po takim skrypcie zostaje ślad w logu. Gdy ktoś zapyta „czy na pewno to zrestartowałeś?”, nie musisz sięgać do pamięci.
Procesy: diagnoza i automatyczne „sprzątanie”
Czasem automatyzacja to po prostu automatyczne „sprzątanie” po aplikacjach, które zostawiają wiszące procesy. Robisz dziś to ręcznie w Menedżerze zadań? Zobacz wariant skryptowy.
Narzędzia do pracy z procesami:
Get-Process– lista procesów,Stop-Process– zakończenie procesów,Start-Process– uruchomienie nowego procesu (np. innego skryptu, programu).
Prosty scenariusz: raz na godzinę sprawdź, czy jakiś proces nie „ucieka” z użyciem pamięci.
param(
[string]$ProcessName = "powershell",
[int]$MemoryLimitMB = 500
)
$procs = Get-Process -Name $ProcessName -ErrorAction SilentlyContinue
foreach ($p in $procs) {
$memMB = [math]::Round($p.WorkingSet64 / 1MB, 0)
if ($memMB -gt $MemoryLimitMB) {
Write-Host "Proces $($p.Id) ($ProcessName) używa $memMB MB – kończę."
Stop-Process -Id $p.Id -Force
}
}
Taki skrypt możesz później dodać do Harmonogramu zadań i wykonać co 30 minut. Jakie procesy sprawiasz, że „pilnujesz wzrokiem”, zamiast zlecić to skryptowi?
WMI/CIM i Win32_*: głębsze dane o systemie
Wiele informacji o systemie (sprzęt, konfiguracja sieci, użytkownicy lokalni, drukarki) znajdziesz dopiero przez WMI/CIM. Do prostych zadań często wystarczą wbudowane cmdlety, ale gdy chcesz szczegółów – sięgasz po Get-CimInstance.
Podstawowy schemat:
Get-CimInstance -ClassName Win32_OperatingSystem
Get-CimInstance -ClassName Win32_ComputerSystem
Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType = 3"
Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "IPEnabled = True"
Przykład raportu podstawowych informacji o serwerze (możliwy do wykonania zdalnie):
param(
[Parameter(Mandatory)]
[string[]]$ComputerName
)
$report = foreach ($Computer in $ComputerName) {
$os = Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName $Computer -ErrorAction SilentlyContinue
$cs = Get-CimInstance -ClassName Win32_ComputerSystem -ComputerName $Computer -ErrorAction SilentlyContinue
if ($null -eq $os -or $null -eq $cs) {
[pscustomobject]@{
ComputerName = $Computer
Status = "ERROR"
OS = $null
Manufacturer = $null
Model = $null
MemoryGB = $null
}
continue
}
[pscustomobject]@{
ComputerName = $Computer
Status = "OK"
OS = $os.Caption
Manufacturer = $cs.Manufacturer
Model = $cs.Model
MemoryGB = [math]::Round($cs.TotalPhysicalMemory / 1GB, 1)
}
}
$report
Z takiego obiektu możesz od razu zrobić CSV, HTML, wysłać mailem. Zastanów się, jakie informacje o stacjach czy serwerach zbierasz dziś „na piechotę” – wersja systemu, ilość RAM, wolne miejsce, wersje BIOS-u?
Łączenie elementów: jeden skrypt – pełna obsługa zadania
Największy zysk daje połączenie pracy z plikami, usługami, rejestrem i WMI w jedno, spójne narzędzie. Przykład z życia: wdrożenie nowej wersji usługi na kilkunastu serwerach.
Co taki skrypt może robić krok po kroku?
- Wczytać listę serwerów z pliku CSV.
- Zatrzymać daną usługę na każdym z nich.
- Windows PowerShell in Action, Third Edition. Manning Publications (2017) – Podstawy języka, obiektowość, pisanie skryptów i modułów
- Learn PowerShell in a Month of Lunches, Third Edition. Manning Publications (2017) – Wprowadzenie do PowerShell, automatyzacja codziennych zadań w Windows
- PowerShell Scripting and Toolmaking. Don Jones and Jeffery Hicks (2017) – Projektowanie narzędzi zespołowych, parametry, obsługa błędów, logowanie
- PowerShell Documentation. Microsoft – Oficjalna dokumentacja poleceń, obiektów, potoków i praktyk skryptowych
Najczęściej zadawane pytania (FAQ)
Od czego zacząć naukę PowerShell, jeśli do tej pory tylko „klikałem” w Windows?
Najpierw odpowiedz sobie na pytanie: które trzy czynności w Windows powtarzasz najczęściej – kopiowanie plików, sprzątanie logów, sprawdzanie miejsca na dysku, odpalanie tych samych aplikacji? Wypisz je na kartce, a potem spróbuj opisać każdą jako: wejście (na czym działa skrypt), logika (co ma zrobić) i wyjście (jaki efekt chcesz dostać). To da ci konkretne mini‑projekty, zamiast abstrakcyjnej „nauki PowerShell”.
Następny krok to konsola. Uruchom PowerShell i zacznij testować proste polecenia (Get-ChildItem, Get-Process, Get-Service). Gdy coś działa tak, jak chcesz, przenieś to do pliku .ps1 w edytorze (np. VS Code). Zadaj sobie pytanie: „co dokładnie chcę zautomatyzować dzisiaj?”, zamiast „czego mam się nauczyć z teorii?”.
Jak wybrać pierwsze zadania do automatyzacji w PowerShell?
Spójrz na swój dzień pracy: co robisz kilka razy dziennie albo co tydzień i jest powtarzalne? Najczęściej są to: sprzątanie katalogów (np. Pobrane, logi), sprawdzanie wolnego miejsca na dyskach, generowanie prostych raportów, powtarzalna konfiguracja nowych komputerów, uruchamianie zestawu narzędzi diagnostycznych. Zastanów się, które z nich zajmują ci realnie 10–15 minut za każdym razem.
Dobre pierwsze projekty to takie, które są często powtarzane, da się je łatwo zmierzyć (czas przed/po) i nie niosą dużego ryzyka. Przykładowo: skrypt do raportu wolnego miejsca na dyskach albo automatycznego porządkowania katalogu z pobranymi plikami jest znacznie bezpieczniejszy niż hurtowe kasowanie danych na serwerze produkcyjnym. Jakie trzy zadania przychodzą ci do głowy w tym momencie?
Czym różni się „jednorazowy skrypt” od narzędzia dla całego zespołu?
Pomyśl, kto ma używać skryptu i jak długo ma żyć. Jeśli piszesz coś „dla siebie na dziś”, możesz pozwolić sobie na prostszy kod, sztywno wpisane ścieżki i minimalną obsługę błędów – ważne, żeby działało i oszczędziło ci czas tu i teraz. To w praktyce zestaw poleceń zapisanych w pliku.
Jeśli jednak skrypt ma być narzędziem używanym przez zespół, potrzebujesz innego podejścia: parametrów (ścieżki, tryb „na sucho”), czytelnych komunikatów dla kogoś, kto nie zna twojej logiki, logowania działań do pliku oraz obsługi błędów, która nie zatrzyma wszystkiego po pierwszym problemie. Spójrz na swój obecny skrypt i zapytaj się: „czy ktoś z zespołu poradziłby sobie z tym beze mnie?”. Jeśli nie, traktuj go jeszcze jako wersję roboczą.
Jak przełożyć ręczne klikanie na konkretny skrypt PowerShell?
Weź jedno zadanie i rozpisz je w trzech krokach: wejście, logika, wyjście. Wejście odpowiada na pytanie: „skąd skrypt wie, na czym ma działać?” – mogą to być ścieżki do katalogów, nazwy serwerów, lista użytkowników, plik CSV lub parametry przekazane w wierszu poleceń. Logika to decyzje: które pliki kopiować, co robić w razie konfliktu nazw, jak reagować na brak uprawnień.
Wyjście to to, co chcesz dostać na końcu: raport CSV, posprzątany katalog, wpisy w logu, wynik na ekranie, który ktoś odczyta ręcznie, lub dane dla kolejnego skryptu. Spróbuj zapisać zadanie jako 2–3 zdania, np.: „Wejście: lista serwerów z CSV. Logika: połącz się, sprawdź miejsce na dyskach, zaznacz alert poniżej 10%. Wyjście: plik CSV z kolumnami Serwer, Dysk, Zajętość, Alert”. Taki opis to w praktyce już projekt skryptu.
Gdzie najlepiej pisać i testować skrypty PowerShell: konsola, ISE czy VS Code?
Zadaj sobie pytanie: czy teraz częściej odpalasz pojedyncze komendy, czy budujesz większe skrypty? Do szybkiego testowania fragmentów kodu najlepsza jest zwykła konsola PowerShell (Windows PowerShell lub PowerShell 7 – pwsh). Tam sprawdzasz, czy dane polecenie działa tak, jak chcesz, zanim zapiszesz je w pliku.
Jeśli tworzysz coś większego, wygodniej jest w edytorze. Windows PowerShell ISE nadal bywa używany i jest dobry do nauki, ale nie jest już rozwijany. Lepiej stopniowo przenosić się do Visual Studio Code z rozszerzeniem PowerShell – dostajesz podpowiedzi składni, debuger, integrację z Git i wygodną nawigację po kodzie. Sprawdź, w czym już pracujesz dzisiaj: notatnik, ISE, a może nic stałego? To podpowie, od czego zacząć zmianę.
Dlaczego w PowerShell „wszystko jest obiektem” i jak to wpływa na skrypty?
Jeśli wcześniej korzystałeś z CMD lub plików .bat, prawdopodobnie myślisz w kategoriach tekstu: linijki, które trzeba parsować, dzielić, przerabiać. W PowerShell cmdlety zwracają obiekty .NET, czyli „rzeczy z właściwościami” – procesy mają Name, Id, CPU, pliki mają Name, Length, LastWriteTime itd. Możesz je filtrować i sortować bez ręcznego obrabiania tekstu.
W praktyce polecenie typu Get-Process | Where-Object CPU -gt 50 | Sort-Object CPU -Descending | Select-Object -First 5 zachowuje się tak, jakbyś miał mini‑Excela w konsoli. Podobnie konwersja do CSV czy JSON dzieje się „automatycznie” na poziomie obiektów. Zadaj sobie pytanie: czy łatwiej ci myśleć „weź wszystkie pliki większe niż 100 MB, posortuj po dacie i pokaż 10 najnowszych”? Jeśli tak – myślenie obiektowe będzie dla ciebie naturalne i znacznie uprości skrypty.
Jak szybko znaleźć odpowiednie cmdlety i zrozumieć, co zwraca dane polecenie?
Podstawowe „kompasy” w PowerShell to Get-Help, Get-Command i Get-Member. Gdy szukasz poleceń dotyczących konkretnej dziedziny (np. usług, procesów, plików), zacznij od Get-Command *service* lub podobnego wzorca. Masz już nazwę? Użyj Get-Help NazwaCmdleta -Detailed, żeby zobaczyć opis, parametry i przykłady.
Kiedy chcesz zrozumieć, co zwraca dane polecenie, podepnij za nim Get-Member, np. Get-Process | Get-Member. Zobaczysz listę właściwości i metod, z którymi możesz pracować w skrypcie. Zastanów się, jak często wpisujesz dziś Get-Help lub Get-Member. Jeśli odpowiedź brzmi „prawie nigdy”, bardzo możliwe, że częściej niż trzeba sięgasz do Google, zamiast wykorzystać wbudowaną dokumentację.






