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

Kasutajaprofiilid ja Powershell

Viimasel ajal olen korduvalt kokku puutunud vajadusega tegeleda lokaalsete kasutajaprofiilidega Windowsi masinates.  Tavaliselt pakuvad huvi kaks teemat:

  • oleks vaja kasutajaprofiil uue konto kätte anda
  • tahaks kustutada ülearused kasutajaprofiilid

Esimest tegevust on sageli vaja, kui toimub Active Directory migratsioon ja kasutajakontod tõstetakse uude AD domeeni.  Siis saab selle tegevuse läbi viia kasutades tarkvara Active Directory Migration Tookit, kasutades Security Translation Wizard’it.

Teise tegevusega on natuke raskem.  Üks viis antud juhul tegutseda on avada My Computer Properties |  Advanced System Settings | User Profiles | Settings .  Avaneb aken, kus on kirjas kõik kasutajaprofiilid lokaalses masinas, nende maht ja viimati kasutamise kuupäev.  Samas võtab mahu arvutamine arvestatava hulga aega, nii et selle järel tuleb (vahel päris kaua) oodata.  Ja lisaks ei saa seal nimekirja sorteerida (see on vaikimisi sorteeritud kasutajakonto nime järgi).  Kui tahaks nimekirja potensiaalselt kustutamisele minevatest profiilidest kiiremini või mingite tingimuste järgi, siis võiks ju kasutada käsurida ja Powershelli.

WMI andmebaas sisaldab nimekirja kasutajaprofiilidest.  Selleks tuleb välja kutsuda WMI klass Win32_UserProfile:

get-wmiobject Win32_UserProfile

Kuna WMI klassidel on alati küljes ülearused atribuudid, siis võiks ekraanile manada ainult olulise info:

Get-WmiObject Win32_UserProfile |
  Sort-Object LastUseTime |
  Format-Table LastUseTime, RoamingConfigured, LocalPath, SID -AutoSize

Tulemuses on näha kasutajaprofiilid sorteerituna viimati kasutatud kuupäeva järgi (vanemad enne) koos faktiga, et kas profiil on Roaming Profile. Tasub tähele panna, et atribuudi LastUseTime sisu võõraste kasutakontode kohta näeb ainult admin-õigustes kasutaja. Puuduseks on see, et atribuut LastUseTime on WMI kuupäeva vormingus, mida Powershell ei suuda ise normaalseks kuupäevaks teisendada.  Õnneks on selle peale mõeldud ning WMI objektid tagastatakse koos lisatud meetodiga, mis oskab kuupäeva konvertida:

$lastused = @{name="last used"; expression={$_.ConvertToDateTime($_.LastUseTime)}}
$roaming  = @{name="Roaming?";  expression={$_.RoamingConfigured}}

Get-WmiObject Win32_UserProfile |
  Sort-Object LastUseTime -Descending |
  Format-Table $lastused, $roaming, LocalPath, SID –AutoSize

Roaming Profile peaks olema muuhulgas salvestatud ka serverisse, nii et neid võib rahulikumalt kustutada.  Edasi oleks vaja valida välja profiilid, mida on viimati kasutatud varem, kui ette antud hulk päevi (näiteks 60) tagasi.  Lisaks võiks välja jätta süsteemsed profiilid ja need, mis parajasti kasutusel on:

Get-WmiObject Win32_UserProfile |
  Where-Object {-not ($_.Loaded -or $_.Special) } |
  Where-Object { $_.ConvertToDateTime($_.LastUseTime) -le (Get-Date).AddDays(-60) } |
  Sort-Object LastUseTime |
  Format-Table $lastused, $roaming, LocalPath, SID –AutoSize

Kui tekib tahtmine näha SID-i asemel kasutajakontot, siis saab seda teha teise WMI klassiga:

Get-WmiObject Win32_Account -Filter ("sid='{0}'" -f $mysid)

Nüüd jääb veel üle sõelale jäänud profiilid ära kustutada:

Get-WmiObject Win32_UserProfile |
  Where-Object {-not $_.Loaded } |
  Where-Object { $_.ConvertToDateTime($_.LastUseTime) -le (Get-Date).AddDays(-60) } |
  Remove-WmiObject

Mõistlik oleks antud tegevuste jada vormistada funktsiooniks või skriptiks, et saaks mugavasti ette anda päevade arvu ja veel mõned parameetrid. Kui tahad alltoodud koodi salvestada skriptiks, siis jäta ära esimene ja viimane rida.

function Get-UserProfile {
    Param (
    	[String[]] $ComputerName = ".",
    	[int] $Days,
    	[ScriptBlock] $Filter,
    	[Switch] $Delete
    )

    $userProfiles = Get-WmiObject Win32_UserProfile -ComputerName $ComputerName
    if ($PSBoundParameters.Keys -contains "Filter") {
    	$userProfiles = $userProfiles |
    		Where-Object $Filter
    }
    if ($PSBoundParameters.Keys -contains "Days") {
    	$userProfiles = $userProfiles |
    		Where-Object { $_.ConvertToDateTime($_.LastUseTime) -le (Get-Date).AddDays(0 - $Days) }
    }

    if ($PSBoundParameters.Keys -contains "Delete") {
      $userProfiles | Remove-WmiObject
    } else {
    	$lastused = @{name="Last used"; expression={$_.ConvertToDateTime($_.LastUseTime)}}
    	$roaming = @{name="Roaming ?"; expression={$_.RoamingConfigured}}
    	$machine = @{name="Computer"; expression={$_.__Server}}
        $uName = @{name="User Account"
                   expression={Get-WmiObject Win32_Account -Filter ("sid='{0}'" -f $_.sid) |
                                 Select-Object -ExpandProperty caption}
        }

    	$userProfiles |
      	    Sort-Object LastUseTime -Descending |
      	    Format-Table $lastused, $roaming, $machine, LocalPath, $uName, SID -AutoSize
    }
}

Kui ülaltoodud funktsioon on laetud Powershelli sessiooni, siis saab seda kasutada nii profiilide nimekirja saamiseks, kui ka kustutamiseks. Kui vormistasid ülaltoodud koodi skriptiks, siis pead hoolitsema, et see skript ka üles leitaks (ja et signeerimata skriptide kasutamine oleks lubatud). Alltoodud näidete eelduseks on, et on laetud funktsioon või skript on sama nimega nagu funktsioon ja asub kaustas, kust Powershell skripte/programme otsib ($env:Path).

# get all roaming user profiles, which are located on server
Get-UserProfile -Filter {$_.RoamingPath -like "\\server\profiles*"}

# get user profiles last used 30 (or more) days ago
Get-UserProfile -Days 30

# get user profiles from several computers
Get-UserProfile -ComputerName server1, server2 -Days 60

# delete corrupted user profiles
Get-UserProfile -Filter {$_.Status -eq 3} -Delete

Tasub tähele panna, et ülaltoodud WMI klass toimib alates Windows Vista-st. Varasemate OS-ide jaoks on vaja alternatiivseid lahendusi. Nagu näiteks Server 2003 Resource Kit’i utiliit DELPROF.  Selle utiliidi jaoks pole vaja ülaltoodud võimlemist, kuna see utiliit oskab ise vanu profiile üles otsida:

delprof /q /i /d:60

Ainuke probleem selle tööriistaga on, et see on ametlikult toetatud vaid Windows 2000/XP või Server 2003 peal.  Windows Vista/Server 2008 ja värskemad pole toetatud.  Kui soovid ilma Powershellita hakkama saada, siis tuleb vaadata 3. partei tööriistade poole, nagu näiteks DelProf2. Selle käsurida sobib algse utiliidiga.

On veel võimalus kasutada VBScriptis kirjutatud skripti, kuigi ka selle puhul on kurdetud, et see ei tööta Windows 7 peal.

Windows 7 ja aeglane Logon

Puutusin kokku järgmise probleemiga:

Osadel Windows 7 masinatel on kasutaja sisselogimine aeglane.  Sümptomina võib logidest leida järgmiseid teateid:

  • Event ID: 6005 – The winlogon notification subscriber <GPClient> is taking long time to handle the notification event (Logon).
  • Event ID: 6006 – The winlogon notification subscriber <GPClient> took xxx second(s) to handle the notification event (Logon).

Kõikidel seotud juhtudel on tegemist situatsiooniga, mis tekib järgnevate tingimuste koosmõjul:

  1. OS on Windows 7
  2. kasutusel on Group Policy ja Folder Redirection
  3. samas masinas rakendub ka Group Policy Preferences (sama või teine GPO)
  4. Sisse on lülitatud järgmine Policy säte:
    User Configuration | Admin Templates | System | Folder Redirection | Do not automatically make redirected folders available offline
  5. Eelmise punkti tõttu on kasutajad oma ärasuunatud kaustadest käsitsi faile/kaustu ühenduseta kättesaadavaks tehtud.

Niipea, kui kasvõi üks nimetatud tingimustest ei ole täidetud, probleemi ei esine.  Seni pole veel probleemile korrektset lahendust leitud.

Minu jaoks on ülatoodud tingimustest kõige kaheldavama väärtusega neljas tingimus.  Sülearvutites toob see paratamatult kaasa viienda tingimuse ning ainult laua-arvutites on antud sättel natuke mõtet.  Nimelt ei ole siis ärasuunatud kaustade sisu siis puhverdatud kohalikku masinasse.  Samas esimene tingimus (Win7) toob kaasa selle, et puhverdatud kaustade sisu kasutatakse serveris paikneva dokumendi asemel, kui lokaalne koopia on värskem.  Kasutaja jaoks tähendab see kiiremat ning sujuvamat kasutuskogemust (muudetud fail sünkroniseeritakse serveriga taustal).

Neljandast tingimusest loobumine kaotab tavaliselt ka viienda (kuna puudub vajadus) ning muuhulgas kaob automaatselt kogu probleem.

Group Policy ja User Account Control (UAC)

Hiljuti juhtus selline probleem: miskipärast ei võetud kasutajale Group Policy logon skriptiga külge võrguketast.  Täpselt sama juhtus ka siis, kui skripti asemel sai proovitud Group Policy Preferences funktsionaalsust.  Lähemal uurimisel selgus, et võrguketas võetakse külge küll, aga Windows Explorer ei tea sellest midagi.  Edasine uurimine viis KB artiklini, mis tõi asjasse valgust.

Nimelt tuleb välja, et võrguketaste külgevõtmine käib kasutaja mandaadi järgi.  Ja alates Windows Vistast on admin-õigustega kasutajatel (sisselülitatud UAC korral) alati kaks mandaati: üks administraatori õigustega ja teine ilma nendeta.  Ehk siis nagu ülalmainitud artikkel väidab, et kui võtta võrguketas külge admin-õigustes, siis tavaõigustes seda võrguketast näha ei ole.  Ja vastupidi.

Probleem aga on hoopis selles, et sama efekt tekib ka siis, kui võrguketas külge võtta Group Policy abiga.  See viitab sellele, et Group Policy rakendamine toimub arvutis enne, kui admin-õigustega kasutajale luuakse ilma admin-õigusteta mandaat ning käivitatakse töölaud.  Kes teab, mis seal veel võib tegemata jääda või vale mandaadi küljes olla…

Et asi veel keerulisem oleks, tuli välja, et kui Group Policy rakendamisel kasutati loopback processing režiimi, siis tulid võrgukettad kasutajale külge.  Seega rakendatakse loopback processing režiimis juba käivitatud töölaua seest (ehk siis mitte-admin õigustes).

Ülalmainitud artikkel väidab, et probleem esineb Windows Vista sees.  Tegelikult ilmneb see ka Server 2008 ja 2008 R2 (seega ka Windows 7) sees.  Ning väidetavasti on sama mure veel ka Windows 8 sees.  Nii et kui võrguketaste külgevõtmisega on probleeme, siis tasub otsida abi ülaltoodud artikli soovitustest.

Kasutajaprofiilide ettevalmistus

Kui me valmistame kasutajatele arvuteid ette ja tahame kasutajatele ka töökeskkonna ette valmistada, siis ka domeeni keskkonnas ei saa päris kõike teha ainult Group Policy abil.  Selle asemel on mõistlikum kohandada Default User profiili.  Teatavasti, kui kasutaja logib arvutisse sisse ning tal ei ole seal kasutajaprofiili, siis talle luuakse uus võttes aluseks Default User profiil.

Minevikus soovitati teha järgmiselt:

  1. luua kasutaja
  2. logida ülalmainitud kasutajaga sisse ning kohadada tema töökeskkond selliseks, nagu tulevikus soovitatakse
  3. logida kasutajaga välja ning administraatori õigustes kopeerida vastloodud profiil Default User profiiliks

Tegelikult toob ülalmainitud protseduur endaga kaasa hulga probleeme ning Microsoft lõpetas nimetatud protseduuri toe alastes Windows XP-st.  Ent kasutajaprofiili kopeerimise funktsioon jäi kasutajaliidesesse alles.

Alates Windows XP SP2-st muudeti Minisetup protseduuri nii, et selle jooksul saab kopeerida lokaalse administraatori kasutajaprofiili muudatused Default User profiili.  Selleks tuleb SysPrep’i vastuste faili lisada vajalikud korraldused, mis on dokumenteeritud KB artiklites 959753 (XP, Vista, Server 2003/2008) ja 973289 (Windows 7).

Kõige jaoks, mida nii paika sättida ei saa, on olemas teised meetodid (nagu näiteks Gropu Policy ja skriptid) ning neid tutvustab oma Michael Murgolo oma Deployment Guys blogi artiklite sarjas.