Veel kord kasutajaprofiilidest

Sai kunagi kirjutatud, kuidas Powershelli abiga kasutajaprofiile üles leida ja kustutada.  Vahepeal on aeg edasi läinud ja uued Powershelli versioonid oskavad samu asju natuke paremini teha.  Sellega seoses sai kirjutatud moodul, mis võimaldab kasutajaprofiilide haldust automatiseerida.  Mooduli leiab Technet Script Gallery’st ja Powershell 5.0 omanikud võivad selle alla tõmmata Powershell Gallery‘st.  Viimaste jaoks on see käsurealt ülilihtsalt teostatav:

#otsi
Find-Module -Name UserProfile

#paigalda
Install-Module -Name UserProfile

Mooduli kasutamine käib järgnevalt:

#Find all user profiles except special system profiles.
Get-UserProfile -Special $false

#Delete all roaming profiles that are not used for 3 months.
$myDate = (Get-Date).AddDays(-90)
Get-UserProfile -Before $myDate -Roaming $true -Loaded $false |
  Remove-UserProfile

#Find user profile of specific user.
Get-AdUser John | Get-UserProfile

#Find all roaming profiles from specific computers.
$session = new-cimsession -ComputerName srv1,srv2 -credential domain\user
Get-UserProfile -CimSession $session -Roaming $true

#Migrate local user account profile into domain user account profile.
$oldAccount = Get-CimInstance Win32_UserAccount -filter "caption='PC\\user'"
$newAccount = Get-AdUser Mati
$oldAccount | Get-UserProfile | Set-ProfileOwner -SID $newAccount.SID

#discover user profile owner by folder name
Get-Item c:\users\kasutaja |
  Select-Object -ExpandProperty FullName |
  Get-UserProfile |
  Get-ProfileOwner

Ülalmainitud moodul töötab ilusti ka Windows 7 ja Server 2008 R2 peal ning võib-olla et isegi Vista/Server 2008 peal, kui sinna Powershell 3.0 paigaldada.  Pole hetkel Vista masinat kuskilt võtta, seega ei saa proovida.

Loodetavasti on antud moodul mugavam kasutada, kui kunagi pakutud skriptinäide.

Advertisements

Powershell kaugtöö ja töövood

Sageli on meil vaja teha automaatselt midagi mitmes masinas.  toome näiteks üsna levinud vajaduse korjata masinatest kokku, et kes on lokaalse administraatorite grupi liikmed.

Alustame kõigepealt ühest masinast.  Siin pole vaja eriti midagi leiutada, sest internetis ringi vaadates leiab hulgaliselt variatsioone sellel teemal.  Valime neist minu arust kõige lihtsama ja kiirema lahenduse:

net localgroup Administrators

Seda tulemust tuleb natuke ilusamaks teha ja ära kaotada käsurea käsu asjasse mittepuutuv eelinfo, tabeli päis ja lõputeade, et kõik läks hästi (plus paar tühja rida)

net localgroup Administrators |
  Select-Object -Skip 6 |
  Where-Object {$_ -and $_ -notmatch "completed successfully."}

Nüüd jääb veel üle võimalus/vajadus muuta tulemus lihtsalt stringidest objektideks, mis muuhulgas teevad vahet domeeni ja lokaalsetel kasutajatel.  Ja teeme selle veel kohe funktsiooniks, et oleks mugavam korduvalt kasutada:

function Get-LocalAdmins {
  net localgroup Administrators |
    Select-Object -Skip 6 |
    Where-Object {$_ -and $_ -notmatch "completed successfully."} |
    ForEach-Object {
      $name = $_.split("\")
      $member = New-Object PSObject -Property @{DisplayName=$_
                                                Name=$name[-1]
                                                Domain=""}
      if ($name.length -eq 2) { $member.domain = $name[0] }
      $member
    }
}

Täiesti töötav lahendus.  Aga meil oleks vaja sama asja mitmes erinevas masinas.  Põhimõtteliselt, kui lisada ülaltoodud koodi lõppu funktsiooni väljakutse või jätta funktsioon ära, siis saaks koodi kirjutada faili ja siis kasutada tavalist Powershelli kaugkasutust:

"server1", "server2" | Out-File servers.txt
$session = New-PSSession -ComputerName (Get-Content .\servers.txt) -Credential domain\admin

Invoke-Command -FilePath .\get-localadmins.ps1 -Session $session |
  Select-Object Name, Domain, PSComputerName |
  Group-Object PSComputerName

Paarikümne masinaga polegi rohkem vaja, aga kui masinaid on rohkem, siis saaks teha ka natuke teistmoodi.  Nimelt saaksime me funktsiooni ümber defineerida töövooks, jättes funktsiooni sisu täpselt samaks.:

#Requires -Version 3

workflow Test-LocalAdmins {
  net localgroup Administrators |
    Select-Object -Skip 6 |
    Where-Object -FilterScript {$_ -and $_ -notmatch "completed successfully"} |
    ForEach-Object {
      $name = $_.split("\")
      $member = New-Object psobject -Property @{DisplayName=$_
                                                Name=$name[-1]
                                                Domain=""}
      if ($name.length -eq 2) { $member.domain = $name[0] }
      $member
    }
}

Ja nüüd saab seda töövoogu kasutada kohe võõraste masinate peal:

"server1", "server2" | Out-File servers.txt

Test-LocalAdmins -PSComputerName (Get-Content .\servers.txt) -PSCredential domain\admin |
  Select-Object Name, Domain, PSComputerName |
  Group-Object PSComputerName

Vaikimisi jookseb töövoog paralleelselt 100 masina peal (Invoke-Command jookseb vaikimisi 32 masina peal).  Lisaks võimaldab töövoog veel muid eeliseid, mida ilma töövoota on raskem teha, ent mis praeguses näites ei leia eriti kasutust.  Loomulikult võib ka Invoke-Command käsule ütelda paralleeltöötluse limiidiks suurema numbri kui 32 ja vajadusel saaks lisada ka parameetri –AsJob (et panna kogu krempel taustal tööle), ent töövoog tundub siiski lihtsam.  Kasvõi juba selle poolest, et sama töövoogu saab kasutada ka ainult lokaalse masina peal (kui parameeter –PSComputerName ära jätta).  Samuti aitab kaasa see, et töövoog võib olla osa imporditud moodulist (nagu funktsioon) töövoogu jooksutavas masinas, aga kaugtöö jaoks on enamasti lihtsam kirjutada skript (fail kettal), sest funktsioon tuleks defineerida ja käivitada eemal olevates (paljudes) masinates.

Töövoogude ainsaks puuduseks võiks pidada vajadust Powershell 3.0 järgi, aga kuna juba Server 2008/Vista võimaldavad antud versiooni kasutada, siis tänapäeval ei tohiks see enam problemaatiline olla.  Samas, kui masinate hulgas on ikka veel Windows XP/Server 2003 masinaid, siis tuleb otsustada tavalise kaugtöö kasuks.

Kasutajate paroolid ja nende aegumine

Mõni aeg tagasi sai kirjutatud sellest, kuidas avastada domeenist kontosid, mida pole ammu kasutatud.  Muuhulgas sai siis ka mainitud, et selliseid kontosid leiab näiteks selle järgi, et nad pole ammu oma parooli muutnud. Parooli muutmine ja selle regulaarne nõudmine on tänapäeval üpris tavaline.  Selle tõttu aga tekivad pidevalt probleemid inimsete (või teenuste) sisselogimisega.  Seda eriti juhul, kui inimene kasutab veebipõhist rakendust, mis ei oska parooli aegumisel pakkuda paroolivahetust. Või siis on probleemid inimestel, kes lähevad natuke pikemaks ajaks kontorist ära ning parool aegub just sel ajavahemikul. Kuidas siis tuvastada, et inimese parool hakkab kohe (varsti) aeguma.  Esimese hooga võiks ju pakkudua, et Powershelli käsk Search-ADAccount on hea mõte.  Ent natuke edasi vaadates tuleb välja, et see oskab otsida juba aegunud parooliga kodanikke, kes enam ei saa sisse.  Meie aga tahaks hoopis teada, kellel varsti hakkab parool aeguma. Ega siin ei jäägi muud üle, kui otsida domeenist kasutajaid ning vaadata, millal nende parool viimasti muudetud sai:

#Requires -Modules ActiveDirectory
Get-ADUser -filter * -Properties PasswordLastSet

Nüüd tuleb vaid juurde vaadata, et milline on paroolipoliitika:

$passwordAge = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge

Ja siis saab arvutada kuupäeva, millal parool aegub ehk siis mitu päeva tänasest veel parool kehtib:

$user = Get-ADUser mati –Properties Mail,PasswordLastSet

$expiresTime = $user.PasswordLastSet.Add($passwordAge) - (Get-Date)

Edasi tuleb tuvastada teavitamisvajadus.  Ütleme, et kasutajat tuleb teavitada, kui parool aegub 5 või vähema päeva pärast:

if ($expiresTime.Days -le 5 ) {
  #teavitame
}

Kas teavitus on ekraani peal tekstiaken või saadetud e-mail, sõltub juba sellest, et kuidas me teavitada soovime. Kui ise ei suuda otsustada, et mitu päeva ette tuleks teavitada, siis võib kasutada ka Windowsi registrisse kirjutatud väärtust:

$notifyDays = Get-ItemProperty -Name PasswordExpiryWarning `
  -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' |
  Select-Object -ExpandProperty PasswordExpiryWarning

if ($expiresTime.Days -le $notifyDays ) {
  # ...
}

Mainitud registri väärtust saab muuta ka Group Policy abil. Ülaltoodu on piisav, kui ettevõttes ei ole kasutusel Fine-Grained Password Policy.  Kui aga on, siis tuleb parooli aegumise tähtaega hoopis kasutaja küljest otsida:

$domainFunctionalLevel = (get-addomain).DomainMode

if ($domainFunctionalLevel -ge 3) {
  ## Windows2008 domain functional level or greater
  $accountFGPP = Get-ADUserResultantPasswordPolicy $user
  if ($accountFGPP) {
    $passwordAge = $accountFGPP.MaxPasswordAge
  }
}

Kui tuvastada ühe kasutaja parooli aegumist näiteks Logon skripti abil, siis tuleks ülatoodud kasutaja leida ühel järgmistest viisidest:

$user = Get-ADUser ([Security.Principal.WindowsIdentity]::GetCurrent().user)

$user = Get-ADUser ("{0}\{1}" -f $env:USERDOMAIN, $env:USERNAME)

Kui tuvastada kõik kasutajad, kelle parool aegub teatud aja pärast, siis peaks tegema midagi sarnast:

$users = Get-ADUser  –Properties PasswordLastSet `
  –Filter {Enabled –eq $true -and PasswordNeverExpires -eq $false -and PasswordExpired -eq $false -and logonCount -ge 1}  |
  Where-Object {! $_.CannotChangePassword}

foreach ($user in $users) {
  # …
}

Mõned aastad tagasi sai kirjutatud Powershelli script, mille eesmärgiks oligi tuvastada kasutajad, kelle parool aegub teatud arvu päevade pärast, ning saata neile e-maili teel teavitus.  Nimetatud skript sai hiljuti laetud ka Techneti skriptide galeriisse.  Nii et kui selle artikli järgi ise omale sobivat skritpti kokku ei oska/taha/suuda panna, siis võib juba valmis skripti alla laadida.

Kontode muutmine domeenis

Vahel on vaja korraga paljusid domeenikontosid muuta.  Näiteks vahetus osakonna juht ja nüüd oleks vaja kõikide osakonna töötajate kontodel juhi atribuut ära muuta:

#Requires -Version 3.0
#Requires -Modules ActiveDirectory

$vana = Get-ADUser "Mihkel Metsik"
$uus = Get-ADUser "Leila Liblikas"

Get-ADUser -Filter {Manager -eq $vana.DistinguishedName} |
  Set-ADUser -Manager $uus

Tasub tähele panna, et kasutajate otsimisel tuleb ette anda konto atribuut DistingushedName, aga uue juhi määramisel on vaja juhi kasutajakontot.

Natuke keerulisemaks läheb asi siis, kui muutmise käigus tuleb igale kontol midagi unikaalset muuta.  Näiteks võib juhtuda, et meil tuleb muuta ära kasutaja atibuut DisplayName.  Kui seni oli see kujul “eesnimi perenimi”, siis nüüd tahaks seda kujul “perenimi, eesnimi”.  Ja teeme seda ainult Hiina töötajatega.  Antud juhul ei saa enam nii lihtsalt läbi kui eelmisel korral:

Get-ADUser -Filter {country -eq "cn"} |
  ForEach-Object {
    Set-ADUser -Identity $_.sid -DisplayName (
      "{1}, {0}" -f $_.GivenName, $_.SurName
    )
  }

Natuke rohkem tööd on ka siis, kui me peaksime muutma kasutajakontode logon nimesid.  Koostame logon nime nii, et võtame eesnimest 5 tähte ja perenimest 2.  Ning võtame seekord ainult ühe grupi liikmed.

Grupi liikmed võivad olla nii kasutajad, arvutid, kui ka teised grupid.  Meie aga tahame vaid kasutajaid.  Lisaks on veel probleem sellega, et Get-ADGroupMember tagastab vale tüüpi objekti, millel pole ees- ja perenime küljes:

Get-ADGroupMember "meie" |
  Where-Object {$_.objectClass -eq "user"} |
  Get-ADUser |
  ForEach-Object {
    $uusnimi = (
      "{0}{1}" -f $_.GivenName.Substring(0,5), $_.SurName.Substring(0,2)
    )
    if (-not (Get-ADUser $uusnimi)) {
      Set-ADUser -Identity $_.sid -SamAccountName $uusnimi
    }
  }

Kontonimede muutmisel tuleb arvestada, et atribuudil SamAccountName on peal 20 märgi piirang ja pikema teksti sisestamiskatsel saame veateate. Samuti tuleks arvestada, et mainitud atribuut peab olema domeeni piires unikaalne ja seetõttu kontrollime, et sellise nimega kasutajat juba olemas ei oleks. Jääb veel võimalus, et sama nime kannab mõni arvuti või grupp, aga seda me siin ei kontrolli. Samuti jääb juba sellise nimega kasutaja olemasolu korral kasutaja logon nimi muutmata.

Juhul kui iga muudetud väärtus on ette teada ja seda ei saa välja arvutada, siis aitab hädast välja juba varem kirjeldatud CSV faili import.  Vaja vaid, et CSV failis olesid õigete nimedega veerud:

Import-Csv c:\modify.csv |
  ForEach-Object {
    Set-ADUser -Identity $_.id -MobilePhone $_.mobile
  }

Ühe liigutuse võiks veel teha.  Nimelt kui meil on rühmapoliitika korralikult juurutatud, siis ei ole kasutajatel enam vaja personaalseid logon skripte.  Korjame need ära:

Get-ADUser -Filter * |
  Set-ADUser -Clear ScriptPath

Kui ülaltoodud näidetest jääb väheks, siis TechNetis on näiteid veel.  Need on küll pärit Server 2008 R2 ajast, aga töötavad ka uuemate ActiveDirectory moodulitega.

Gruppi kuulumise kontroll

Domeeni keskkonnas on sageli vaja tuvastada, kas kasutaja kuulub mingisse gruppi või kes on mingi grupi liikmed.  PowerShelli abil on seda lihtne kontrollida:

#Requires –Modules ActiveDirectory

Get-ADGroup "minu grupp" | Get-ADGroupMember
#või siis
Get-ADGroupMember "minu grupp"

Get-ADUser mina | Get-ADPrincipalMembership

Asi läheb natuke keerulisemaks, kui grupid kuuluvad gruppide sisse.  Ülaltoodud näited annavad vaid grupi vahetud liikmed (või grupid, kuhu kasutaja vahetult kuulub) ning välja jäävad grupid, mis kuuluvad teiste gruppide sisse.

Teoreetiliselt on ka see situatsioon lihtne.  Vaja vaid võtta saadud nimekiri ning ka nende liikmelisus tuvastada:

Get-ADUser mina |
  Get-ADPrincipalGroupMembership |
  Get-ADPrincipalGroupMembership

aga nii läheb kaotsi esimene kiht liikmelisust.  Ka seda annab parandada:

Get-ADUser mina |
  Get-ADPrincipalGroupMembership |
  Foreach-Object {
    $_
    Get-ADPrincipalGroupMembership $_
  } | Sort-Object –Unique

Jääb vaid probleem, et kui liikmelisuse kihte on rohkem, siis tuleks sama tegevust korrata mitu korda.  Kordade arv võib sõltuda vajadusest ning sellist ühte rida on tüütu kirjutada.  Seega võiks luua rekursiivse funktsiooni, mis vajadusel iseennast välja kutsub:

#Requires -Modules ActiveDirectory

Function Get-GroupMembership {
  Param(
    $level = 1
  )
  PROCESS {
    $_
    if ($level –ne 0) {
      Get-ADPrincipalGroupMembership $_ |
        Get-GroupMembership –level ($level-1) |
        Sort-Object -Unique
    }
  }
}

Ja nüüd jääb üle vaid seda funktsiooni kasutada:

Get-ADUser mina | Get-GroupMembership –level 2

Get-ADGroup miskigrupp | Get-GroupMembership –level 4

# kui vaja saada liikmelisus ilma algse objektita:
Get-ADUser mina | Get-GroupMembership –level 3 | Select-Object -skip 1

if (
    Get-ADUser mina |
      Get-GroupMembership –level 3 |
      Where {$_.name –like "grupp1"}
  ) {
    Write-Host "Kasutaja mina kuulub gruppi grupp1"
}

Grupi liikmete lahtiharutamise korral on asi palju lihtsam. Nimelt on käsul Get-ADGroupMemeber olemas käsurea võti -Recursive.  Ainsaks puuduseks on see et nii saame teada vaid kasutajad, kes gruppide hierarhiasse kuuluvad. Küll aga on see piisav, et kontrollida, kas kasutaja kuulub (kaudselt) gruppi:

if (
  Get-ADGroupMember grupp1 -Recursive |
    Where-Object {$_.name –like "*mina*"}
) {
  write-host "Kasutaja 'mina' kuulub gruppi 'grupp1'"
}

Veelgi mugavam on ülaltoodud kood vormistada funktsiooniks ja siis seda kasutada:

Function Test-IsGroupMember {
  Param(
      [parameter(Mandatory=$true)]
      [Microsoft.ActiveDirectory.Management.ADGroup]
    $group,
      [parameter(ValueFromPipeline=$true)]
      [Microsoft.ActiveDirectory.Management.ADUser]
    $user
  )
  PROCESS {
    [bool](
      Get-ADGroupMember $group -Recursive |
        Where-Object {$_.SID –eq $user.SID}
      )
  }
}

$mina = Get-ADUser (whoami).split("\")[1]
$mina | Test-IsGroupMember (Get-ADGroup grupp1)

Kasutajakontode loomine domeenis

Tahan tuua mõned näited, kuidas domeenis objekte hallata skriptide abil.  Tasub vist mainida, et minu näidete töötamise eelduseks on Windows Server 2008 R2 (või 2012) haldusvahenditega kaasatuleva Powershelli mooduli ActiveDirectory olemasolu.

Alustame kasutajakonto loomisest:

#Requires –Version 2.0
#Requires –Modules ActiveDirectory

# laeme vajadusel AD mooduli
if ($Host.Version.Major -eq 2) {
  Import-Module ActiveDirectory
}

New-ADUser –Name Mari

Kogu töö teeb ära käsk New-ADUser ning uue konto loomiseks rohkem polegi vaja.  Tuleb lihtsalt tähele panna, et kuna kontole ei määratud parooli, siis on konto välja lülitatud ja seda ei saa sisse lülitada enne, kui kontole on määratud parool.  Lisaks on nii logon nimi (sAMAccountName) kui ka konto nimi mõlemad samad.  Ning ees- ja perenime pole üldse.  Selle vea saab kohe parandada:

$userParams = @{
  Name = "Kati Kallike"
  SamAccountName = "Kati"
  GivenName = "Kati"
  SurName = "Kallike"
}

New-ADUser @userParams

Kui me juba skripte teeme, siis tõenäoliselt on meil korraga rohkem kasutajaid, keda luua ning üks lihtsamaid viise kasutajaid sedasi teha on CSV faili import.  Teeme näiteks järgmise CSV faili:

GivenName,Surname,Department,SamAccountName
Mati,Maasikas,Müük,mati
Juhan,Juurikas,Juhatus,juhan

ja impordime selle:

Import-Csv c:\users.csv -Encoding Default |
  select *,
    @{name="Name"; e={"{0} {1}" –f $_.GivenName, $_.SurName }},
    @{name="DisplayName"; e={"{0} {1}" –f $_.GivenName, $_.SurName }} |
  New-ADUser

Töö on lihtne selle tõttu, et CSV failis on veergude nimed viidud kooskõlla käsu New-ADUser parameetritega (ja tuletatud pikk nimi).

Teine võimalus uuele kontole atribuute ette anda on kasutada valmis kontot kui malli.  Selleks tuleb käsule New-ADUser anda ette kasutajakonto, mille küljest olemasolevad atribuudid võtta:

$template = Get-ADUser _usertemplate -Properties City, ProfilePath, MemberOf

Import-Csv c:\users.csv |
  New-ADUser -Instance $template

Nii täidetakse atribuudid, mis sageli on erinevatel kontodel ühised.  Muuhulgas ka gruppidesse kuulumised.  Ent kuna kasutajale pole antud parooli, siis on kasutajakontod ikka veel välja lülitatud.  Parooli lisamiseks on mitu võimalust.

Parooli saab skripti käivitaja käest küsida:

#Requires –Version 2.0
Read-Host -AsSecureString -Prompt "Ütle üks ilus parool"

(Get-Credential -Message "Ütle üks ilus parool" -UserName kasutaja).Password

Samas saab parooli ka näiteks tekstifailist (või parooligeneraatorist) lugeda:

ConvertTo-SecureString -String (Get-Content c:\parool.txt) -AsPlainText -Force

Paneme parooli lisamise eelnevale skriptile otsa:

#Requires –Modules ActiveDirectory

Import-Csv c:\users.csv |
  New-ADUser -AccountPassword (Get-Credential -UserName kasutaja).Password -Enabled $true

Nii saab kasutajakontod ka kohe sisse lülitada.  Samas on võib-olla hea mõte enne kasutajale parooli määramist välja mõtelda, kuidas parool kasutajani toimetada.  Ja las ta niikaua olla välja lülitatud ning ilma paroolita.

Loome nüüd uued kasutajad kindlasse OU-sse ning paneme nad ka gruppi :

#Requires –Modules ActiveDirectory
$rootdn = (Get-ADDomain).DistinguishedName

Import-Csv c:\users.csv |
  New-ADUser -PassThru -Path "ou=uued,ou=kasutajad,$rootdn" |
  Add-ADPrincipalGroupMembership -MemberOf "kõik töötajad"

Kui me nüüd mõne aja pärast avastame, et mõned atribuudid jäid kohe paika panemata, siis tuleb seda teha hiljem. Ja käsk Set-ADUser ei ole enam nii vastutulelik ning ei ole nõus otse torust muudetavaid väärtusi lugema.  Lisaks ei ole kõikide kasutajakonto atribuutide jaoks ka eraldi käsureavõtit. Siin tuleb natuke kavaldada. Loome kõigepealt sobiva CSV faili:

id, email, parool
mati,"mati@firma.ee",Par0ol33
"cn=Juhan Juurikas,ou=kasutajad,dc=firma,dc=ee","juhan@firma.ee",pAro0like

ja siis anname selle käsule ette. Tasub tähele panna, et parameeter -Add lubab lisada mistahes konto atribuute – tuleb vaid teada nende atribuutide LDAP-nimesid (need erinevad graafilise kasutajaliideses ja Powershellis kasutatavastest nimedest):

Import-Csv c:\modify.csv | ForEach-Object {
  Set-ADUser -Identity $_.id -Add @{mail=$_.email}
}

Sama lugu on parooli tagantjärele muutmise ja kontode avamisega:

Import-Csv c:\modify.csv | ForEach-Object {
  Set-ADAccountPassword -Identity $_.id `
                        -Reset `
                        -NewPassword (ConvertTo-SecureString -AsPlainText -Force $_.parool) `
                        -PassThru |
  Enable-ADAccount
}

Käsurea atribuudi -Reset kasutamine eeldab, et skripti käivitaval kasutajakontol on õigus muuta teiste kasutajate paroole. Kui skriptiga tahetakse muuta kasutaja enda õigustes tema parooli, siis tuleb see asendada atribuudiga -OldPassword (ja lisada kehtiv parool).

Vanad kontod domeenis

Aeg-ajalt on domeenist vaja üles leida kasutajakontod, kes pole tükk aega ennast näole andnud.  On nad siis ettevõttest lahkunud või on nende projekt lõppenud ja neil pole enam ligipääsu vaja.  Tavaliselt unustatakse mõlemal juhul süsteemiülemale teatada, et konto võib kinni panna.

Sarnane probleem tekib ka arvutikontodega.  Kui arvutid maha kantakse ning ära müüakse, siis ununeb sageli masina domeenist väljavõtmine.  Aga ConfigMgr’i jaoks tähendab see terve hulk zombisid, mida ei ole enam vaja.

Alates Windows Server 2003-st on haldusvahendite hulgas ka hunnik käsurea utiliite, mis antud juhul abiks on:

dsquery user "ou=kasutajad,ou=firma,dc=firma,dc=ee" –inactive 13

dsquery user –stalepwd 90

Ülaltoodud näites esimene rida otsib välja kasutajakontod, mis pole viimase 13 nädala jooksul sisse loginud ning teine rida otsib välja kasutajad, mis viimase kolme kuu (90 päeva) jooksul pole parooli muutnud.  Sama saab teha ka arvutikontodega.

tuleb vaid tähele panna, et kui inimesele on tehtud konto ja sellega pole kunagi sisse logitud, siis ei leia –inactive võtmega otsimisel seda kontot üles.  Nimetatud võti kasutab domeenikonto atribuuti lastLogonTimestamp, kus on kirjas et millal konto viimati sisse logis.  Ja kui ta pole kunagi loginud, on see atribuut tühi.

Masinakontodega on natuke lihtsam, kuna masinad käivad regulaarselt iga 30 päeva tagant oma parooli muutmas.  Kui nad pole seda teinud, siis pole nad viimase kuu jooksul domeenikontrollerit näinud.

Kui nüüd kontod üles leitud, siis peaks need välja lülitama.  Siis on kindel, et need ei saa enam domeeni sisse ja ühtlasi tuleb sageli ka välja, et kui konto(d) on tegelikult vajalikud, siis võetakse meiega varsti ühendust:

dsquery user –inactive 16 | dsmod user –disabled yes

dsquery computer –stalepwd 90 | dsmove –newparent "ou=teadmata kadunud"

Sellisel viisil võib muidugi juhtuda, et osa leitud kontosid juba on välja lülitatud.  Seega oleks vaja natuke võimsamat vahendit.

Windows Server 2008 R2 ja tema kaughaldusvahendid sisaldavad Powershelli moodulit ActiveDirectory.  Antud juhul on see väga abiks.  Vaja vaid, et vähemalt üks domeenikontroller oleks Windows Server 2008 R2 või siis et vähemalt ühele domeenikontrollerile oleks paigaldatud Active Directory Gateway Management Service.  Ja siis saame teha järgmist:

#Requires -Modules ActiveDirectory

Search-ADAccount -AccountInactive -TimeSpan 90.00:00:00 -UsersOnly

$ammu = (Get-Date).AddDays(-90)

Get-ADUser -Filter {logonCount -ge 1 -and LastLogonDate -le $ammu} |
  Move-ADObject -TargetPath "ou=kadunud hinged"

Get-ADUser -Filter {Enabled -eq $true -and PasswordLastSet -le $ammu} `
  -SearchBase "ou=IT,ou=kasutajad,dc=firma,dc=ee"

Get-ADComputer -Filter {PasswordLastSet -le $ammu} | Disable-ADAccount

Ülaltoodud näites leiab 7. rida kasutajakontod, kes on vähemalt korra sisse loginud ja viimane sisselogimine on toimunud 90 päeva tagasi või varem.  Leitud kontod tõstetakse eraldi OU-sse.

10. rida leiab kasutajakontod kindlast OU-st, kes ei ole välja lülitatud ja kelle parool on viimati vahetatud 90 päeva tagasi või varem.

Viimane rida leiab arvutikontod mis ei ole viimase 90 päeva jooksul domeenikontrollerit näinud (kui oleks, siis oleks ka parool juba muudetud) ning lülitab need välja.

Loomulikult tekib küsimus, et milliseid atribuute veel on, mida kasutada saaks.  Küsime seda Powershelli käest:

#Requires -Modules ActiveDirectory

Get-ADUser -Identity mina -Properties *

Siin tuleb tähele panna, et üks tagastatud atribuut on selline, mis arvutatakse atribuudi nTSecurityDescriptor pealt: see on CannotChangePassword.  Seda atribuuti ei saa filtris kasutada, küll aga saab seda kontrollida mööda toru järgmises käsus:

Get-ADUser -Filter * -Properties CannotChangePassword |
  Where-Object {$_.CannotChangePassword}

Tulemuseks on kasutajate nimekiri, kes ei saa ise oma parooli muuta.

Täpsemalt infot filtri võimalustest ja atribuutidest, mida võib Active Directory objektide küljest leida, saab järgmistest Powershelli spikri teemadest (või veebist):