na początek zaprezentuję… początek. całkiem logicznie q:
find-UnusedADObjects.ps1
<# .NOTES ################################################################################ # script to search unused AD objects v2.o # # purpose: maintenance # # nexorek@gmail.com # # o8.o5.2o15 # # - whole code rewritten # # # ################################################################################ .PARAMETER scope defines the query. possible are queries for: inactive computers ("Computers"), inactive users ("Users"), users with nonExpiring password ("nonExpiring") and do all the queries ("all") .PARAMETER unusedTime defines the period of maximum inactivity based on lastLogon attribute .PARAMETER toScreen data are saved to files by defaults. use toScreen to output to screen #> #Requires –Version 3 [cmdletBinding()] param( [validateSet("Computers","Users","nonExpiring","All")][string]$scope="All", [validateRange(1,1096)][int]$unusedTime=96, [string[]]$OUsTOSKIP=(gc .\find-UnusedADObjects.OUsToSkip), [switch]$toScreen, [switch]$splitFiles #additional option to split/extract files with 'skipped' objects in seperate files #for easier reviewing. better reporting. ) #maxium number of days the user/computer may be inactive $MAXUSERIDLE=$unusedTime $MAXCOMPUTERIDLE=$unusedTime #output file for further processing $fdate=get-date -Format ("yyyyMMddhhmm") $FILE_NON_EXPIRING=".\fado-nonexpiring-$fdate.csv" $FILE_INACTIVE_USERS=".\fado-inactiveUsers-all-$fdate.csv" $FILE_INACTIVE_COMPUTERS=".\fado-inactiveComputers-$fdate.csv" $usedFiles="" #list of OU names, that should be skipped during object scan. [regex] $ouRX = ‘(‘ + (($OUsTOSKIP |foreach {[regex]::escape($_)}) –join “|”) + ‘)’ #prepare LDAP filters for queries $incativeFlag="(&(!userAccountControl:1.2.840.113556.1.4.803:=2)(userAccountControl:1.2.840.113556.1.4.803:=65536))" $loggedBeforeFlag="(&(objectCategory=[OBJCATEGORY])(objectClass=user)(|(lastLogonTimeStamp<=[INTERVAL])(!lastLogonTimeStamp=*)))" $searchProperties="sAMAccountName,displayname,distinguishedname,lastLogonTimeStamp,passwordlastset,objectclass" # Convert the local time to UTC format because all dates are expressed in UTC (GMT) format in Active Directory $currentDateUtc = (get-date).ToUniversalTime() $lastLogonIntervalLimit = $currentDateUtc.AddDays(-$MAXUSERIDLE).ToFileTime()
po pierwsze nazwa skryptu z przyjętą w PS notacją. korzyść – bardziej intuicyjne wykorzystanie.
po drugie bardziej rozbudowane komentarze. odpowiednio zrobione są bardzo wygodne – korzystając z polecenia get-help wyświetli się pełny opis. każda wersja PS wprowadza coraz więcej obsługiwanych słów kluczowych, a żeby dowiedzieć się o całości należy poszukać ’comment based help’. niektórzy mogą powiedzieć – 'eeee, jestem zbyt leniwy na takie szlaczki’. zatem zwróćcie uwagę na to, jak opisany jest parametr 'scope’ a jak 'splitFiles’:
- ’scope’ jest wyniesiony na początek , do 'comment based help’
- ’splitFiles’ jest opisany 'na lenia’ jako komentarze tuż pod opcją
obie wersje są obsługiwane przez get-help, więc jak się okazuje – nawet nie trzeba rysować szlaczków. wystarczy minimum wysiłq aby na szybko opisać po co jest dany parametr – z korzyścią dla twórcy jak i dla end-usera.
jest jeszcze jedna metoda tworzenia komentarzy, ale wydaje mi się, że to jeden z pomysłów, który się nie przyjął i został porzucony. jego zastosowanie jest, delikatnie mówiąc, niszowe:
param( [parameter(mandatory=$true,HelpMessage="tutaj można wpisać jakiś komentarz")][string]$variable )
wiadomość można zobaczyć na dwa sposoby – badając kod źródłowy skryptu, lub:
jeśli wartość zdefiniowana jest jako obligatoryjna ORAZ nie wprowadzimy jej ORAZ po wyświetleniu komunikatu wpiszemy ’!?’ [SIC!]:
C:\...ive\_ScriptZ :))o- .\test-helpmessage.ps1 cmdlet test-helpmessage.ps1 at command pipeline position 1 Supply values for the following parameters: (Type !? for Help.) variable: !? tutaj moĹĽna wpisaÄ┼ jakiĹ> komentarz variable:
wygląda na to, że ktoś miał pomysł… tylko inni go nie podzielili (;
„#Required -version 3” zapewnia kompatybilność. nie bawiłem się w ładowanie modułów bo zakładam, że zostaną załadowane automatycznie [od ver. PS3.o] oraz wykorzystane jest kilka technik zapisu, które nie jestem pewien czy zadziałają w PS2.o. to zabezpieczy przed przypadkowym odpaleniem na starej wersji.
następnie parametry. nie tylko są, ale są zdefiniowane w bardziej rozbudowany sposób – część z nich ma określone ograniczenia poprzez validateSet i validateRange. to kolejna rzecz, która nie służy wyłącznie dla zaspokojenia puryzmu i widzi-mi-się. jedną z najwygodniejszych cech PS jest dokończanie TABem. jeśli zdefiniowany jest validateSet, to szybko można całe polecenie wpisać: „find-u<TAB dokończy nazwę skryptu> –s<TAB dokończy nazwę parametru> <TAB będzie przerzucał pomiędzy zdefiniowanymi wartościami>”. takie niuanse są prawdziwą SiłąPowłoki (;
inną ciekawostką jest zdefiniowane zmiennej, której standardowa deklaracja jest kawałkiem kodu:
[string[]]$OUsTOSKIP=(gc .\find-UnusedADObjects.OUsToSkip)
zacznę od tego, że jestem przeciwnikiem takiego pisania – braqje tu obsługi błędów. ale w całym skrypcie darowałem sobie obsługę błędów – zupełnie nieedukacyjnie… ale to na inny wpis, kiedyś. drugi błąd popełniony w tej linijce to wykorzystanie aliasu 'gc’. aliasy mogą być globalnie przedefiniowane więc dla pewności w skryptach powinno się ładnie używać pełnych, prawidłowych nazw commandletów. trzecią 'głupią’ rzeczą, której w praktyce nie robię i jest tutaj bardziej do celów edukacyjnych, jest wyniesienie zmiennych do oddzielnego pliq.
find-UnusedADObjects.OUsToSkip
Servers _UnusedObjects Service Accounts
w starej wersji pliq było wewnątrz: $OUsToSkip=”Servers”,”_UnusedObjects”,”Service Accounts” . kiedy należy wynieść wartości do zewnętrznego pliq? kiedy skrypt będzie zaszyfrowany lub podpisany certyfikatem. czyli nie będzie można w nim dokonać żadnej modyfikacji. należy wtedy jednak dobrze opisać w helpie jak taki plik ma się nazywać, co ma zawierać itd. sporo problemu. a ponieważ zazwyczaj celowo oddaję źródła, zależy mi na 'kompaktowości’ rozwiązania – potęga skryptów to często fakt, że jest to *pojedynczy*, mały plik. niemniej warto wiedzieć, że tak również można zrobić, i to w całkiem prosty sposób.
oczywiście oprócz pliq można po prostu podać tablicę stringów jako parametr… ale niezbyt to wygodne w codziennym użyciu.
kolejne drobiazgi w deklaracjach to np. fakt, że pliki będą się odkładać a nie nadpisywać, ponieważ zawierają datę w nazwie.
dalej widać regex który omówię w późniejszych częściach, deklaracje filtrów wyszukiwania, $searchProperties – które, jak się finalnie okaże, wyniesione w tym miejscu pozwoli w bardzo łatwy i szybki sposób poszerzyć funkcjonalność, jeśli klient nagle zażyczy sobie w raporcie dodatkowych danych.
i to na tyle w tej części. niby tylko 'komentarze i deklaracja zmiennych’ a okazuje się, że już w tej fazie, jest wiele istotnych detali na które należy zwrócić uwagę aby skrypt był wygodny w użyciu i przejrzysty.
eN.