Mis keskkonnas mu skript jookseb

Iga Windowsi süsteemiülem peab oskama PowerShelli kasutada.  Kui ta seda ei oska, siis ei saa ta ennast Windowsi süsteemiülemaks nimetada.  Ja vähehaaval nad seda ka õpivad (kui nad juba ei oska).

Kõik on väga tore, kuni me teame, millises masinas me oma asju jooksutame.  Aga kui me oleme jõudnud juba sinnamaale, et koodi jooksutatakse automaatselt, siis läheb elu keerulisemaks.  Nimelt tuleb siis hakata tuvastama, et mis keskkonnas kood parasjagu jookseb.  Sest kui seda mitte teha, siis võib kood mitte töötada.

Vanasti oli lihtne, tuli vaid tuvastada Powershelli versioon.  Selleks oli kaks meetodit:

if ($PSVersionTable.PSVersion -lt "5.0") {
    throw "see on vale PowerShelli versioon"
}

#Requires -Version 5.0

Neist esimese puhul ei pea tingimata skripti tööd katkestama.

Windows 8-st alates hakkasid Powershelli moodulid kaasa tulema ka opsüsteemiga.  Samuti on vaja moodulite olemasolu kontrollida juhul, kui kasutada mõnd iselisatud funktsionaalsust.  Ka seda saab kahte moodi teha:

if (-not (get-module Hyper-V -ListAvailable)) {
    throw "Vajalik funktsioon puudu, katkestan"
}

#Requires -Modules Hyper-V

Vajadusel saab kontrollida ka mooduli versiooni:

if (-not (
    Get-Module Hyper-V -ListAvailable |
      Where-Object Version -EQ "2.0.0.0"
    )) {
      throw "Vajalik moodul puudu, katkestan"
    }

#Requires -Modules @{ModuleName="Hyper-V";ModuleVersion="2.0.0.0"}

Tasub tähele panna, et #Requires kontrollid töötavad ainult salvestatud skripti sees ning neid kontrollitakse juba skripti laadimisel.  See seab nende kasutamisele mõningad piirid.

Kui moodulite halduseks on oma koodihoidla, siis saab muidugi lihtsamalt:

#Requires -RunAsAdministrator
#Requires -Modules PowerShellGet
Find-Module userprofile -MinimumVersion 1.0 -Repository PSGallery |
    Install-Module -Force -Scope AllUsers

Get-InstalledModule | Update-Module

Aga vahel ei piisa ka mooduli olemasolu/versiooni kontrollist.  Näiteks on Windows 8.1-ga kaasas olevas moodulis (NetTCPIP) olemas käsk Test-NetConnection, mida Windows 8-ga kaasas olevas moodulis pole.  Aga mooduli versiooninumber on mõlemal sama.  Siis tuleb kontrollida konkreetse käsu olemasolu:

if (Get-Command Test-NetConnection -ErrorAction SilentlyContinue) {
    #olemas, võib tegutseda
} else {
    Write-Error "käsk puudu"
}

Get-Command telnet.exe

Vahel oleks vaja hoopis teada, kas mõni opsüsteemi funktsioon on antud masinas olemas või puudu.  Windows Serveri haldusliidesed on funktsionaalsustest eraldi paigaldatavad ning võivad paikneda hoopis teises masinas.  Serveriga on asi lihtne.  Kui skripti jooksutavas masinas on ServerManager moodul (seda saab ka klient-OSile paigaldada, kui tõmmata omale RSAT)

if (-not (Get-WindowsFeature -Name RSAT-DNS-Server).Installed) {
    Install-WindowsFeature -Name RSAT-DNS-Server
}
#olemas, võib tegutseda

Klient-OSi puhul on see natuke keerulisem:

#Requires -RunAsAdministrator
if ((Get-WindowsOptionalFeature -Online -FeatureName telnet*).state -eq
    [Microsoft.Dism.Commands.FeatureState]::Disabled) {
      Enable-WindowsOptionalFeature -Online -FeatureName TelnetClient
    }

Nüüd jääb veel vaid üle kontrollida, et kas meil on käes server-OS või midagi muud:

if ((Get-CimInstance Win32_OperatingSystem).ProductType -eq 1) {
    "Workstation"
} else {
    "DC or Server"
}

#Requires -RunAsAdministrator
if ((Get-WindowsEdition -Online).Edition -like 'Server*') {
    "on server"
} else {
    "klient, vist"
}

Olen seni teadlikult vältinud OS versiooni kontrolli.  nagu ülaltpoolt näha, pole seda enamasti vaja.  Ent arvestades Windows 10 (ja nüüd kohe varsti ka Server 2016) väljalaske tsükliga, võib seda vaja minna:

if ([environment]::OSVersion.Version -ge "6.3") {
    "Windows 8.1 või värskem"
}

Õnnetuseks otsustas Microsoft antud viisile panna peale kontrolli: kui rakendus ise ei deklareeri, et ta tunneb uuemaid OS versioone, siis valetatakse talle versiooniks alati “6.2.9200” ehk siis Windows 8/Server 2012.  Lisaks on Windows 10 versiooninumbri algus kõigil väljalasetel sama, muutub ainult BuildNumber.  Seda aga saab kontrollida:

switch ((Get-CimInstance Win32_OperatingSystem).BuildNumber) {
    10240 {"Win10 RTM"}
    10586 {"Win10 1511"}
    14393 {"Win10/Svr16 1607"}
    15063 {"Win10 1703"}
    default {"Insider build"}
}

Get-CimInstance kasutamine võimaldab teha kontrolli ka eemalt.  Samamoodi saab ka versiooninumbri kätte.  Tuleb lihtsalt arvestada, et CIM tagastab versiooninumbri kui stringi ja see tuleb ise versiooninumbriks teisendada, kui seda vaja peaks olema:

(Get-CimInstance Win32_OperatingSystem).Version -as [version]
Advertisements

PowerShell Linuxile

Ja ongi saanud tõeks see, mida juba mõnda aega oodatud on.  Powershell on kättesaadav Linuxi ja Mac OS X platvormile.  Koos sellega on Powershelli lähtekood tehtud avalikuks ning on saadaval GitHubis Powershelli projekti saidis.

Hetkel on saadaval veel alfa-staatuses kood (v6.0.0-alpha.9), ent kuna tegemist on avatud koodiga arendusprojektiga, siis iga hetkega areneb see edasi.

Vastav info on väljas ka Powershelli kodulehel .

Muuhulgas tasub mainida, et koos Windows 10 aastapäeva väljalaskega tuli välja ka Windows Management Framework (WMF) 5.1 .  Varasematele platvormidele on praegu saadaval vaid eelvaate versioon.  Ning Windows 7 ja Server 2008-le paigaldamisel eeldatakse, et WMF 4.0 on masinas juba olemas…

Nagu ka varasemate versioonidega, ei tasu seda installida Exchange või Sharepoint serverisse.

Powershell 5.0 lõpuks valmis

Sel nädalal sai valmis Windows Management Framework (WMF) 5.0, mis muuhulgas sisaldab Powershell 5.0 versiooni.  Toetatud on Windows Serveri (2008 R2 kuni 2012 R2), ning pärast klientide sooviavaldust on toetatud ka Windows klient-OS (7 ja 8.1).

See versioon asendab Powershell 5.0 Production Preview versiooni (seda pole vaja enne maha võtta).  Seetõttu on paigaldamise eeltingimused enam-vähem samad.  Server 2008 R2 puhul tuleb enne masinasse paigaldada WMF 4.0 .  Lisaks tuleb arvestada, et Powershell WorkFlow ja DSC vajavad mõlemad sisselülitatud WinRM-i.

Kui funktsionaalsuse osas tekib mõtteid, mida tahaks arendajatega jagada, siis UserVoice saidis on eraldi Powershelli foorum, mis asendab endist Microsoft Connect saiti.

Powershell 5 – Production Preview

Windows 10 on juba üle kuu aja väljas, ent varasematele Windowsi versioonidele pole senimaani uut Powershelli versiooni.  Nüüd siis lasti välja Windows Management Framework (WMF) 5.0 Production Preview, mis ei ole küll veel valmis versioon, aga on Microsofti poolt toetatud ning töökeskkonnas kasutuskõlblik.

WMF 5 töötab Windows 7 SP1 või värskema operatsioonisüsteemi peal ning vajab töötamiseks .NET Framework 4.5.

Muuseas tasub ka ära märkida, et Powershellil on nüüd juba mõnda aega oma koduleht, mis teeb info leidmise mõnejagu lihtsamaks.

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.

Powershell 5 Preview April 2015

Järjekordne versioon Windows Management Framework 5.0 eelvaatest.  On möödunud aasta esimesest eelvaatest ja seekord saab seda paigaldada ka Windows 7/Server 2008 R2 ja Server 2012 peale.

Uusi asju on juurde tulnud päris palju.  Muudatusi on nii väikeseid kui suuri.  Peamised suured asjad on ikka Desired State Configuration ja moodul PowershellGet.  Aga on ka pisikesi asju, nagu näiteks Get-Clipboard, Set-Clipboard ja New-TemporaryFile.

Nagu eelvaatega ikka, kui leiad midagi, mida tahad muuta, siis kirjuta aadressil https://connect.microsoft.com/PowerShell/Feedback , või siis vähemalt hääleta juba kirjutatud asjade seas neid, mis ka Sinu arust vajalikud on.

Powershell ja sündmuste logid

Aeg-ajalt on vaja otsida sündmuste logidest teatud kindlaid sündmusi.  Ja vahel oleks hea ka ise sündmusi logida.

Vaatame kõigepealt, kuidas sündmusi leida.  Selleks on Powershellis kaks käsku: Get-EventLog ja Get-WinEvent.  Esimest neist tuleks kasutada ainult juhul, kui on vaja otsida sündmusi Windows Server 2003 logidest.  Ja Server 2003 tugi lõpeb sel aastal ära.

Kõigi uuemate OS-ide puhul tuleks kasutada teist käsku.  See oskab otsida sündmusi ka Event Tracing for Windows logidest.  Ja neid logisid on tänapäeval palju.  Alustamegi siis sellest, et otsime meid huvitava logi üles:

Get-WinEvent -ListLog *
Get-WinEvent -ListLog Application

Nüüd teades logi nime, saame sealt otsida sündmusi.  Piirame selle otsimise mingi arvuga, et mitte liiga kaua oodata:

Get-WinEvent -LogName Application -MaxEvents 10
Get-WinEvent -LogName "Windows Powershell" -MaxEvents 10

# vanemad sündmused enne
Get-WinEvent -LogName Application -MaxEvents 10 -Oldest

Tavaliselt ei huvita meid kõik logisse kirjutatud sündmused, vaid ikka spetsiifilised.  Lisaks võivad huvipakkuvad sündmused olla kirjutatud ka mitmesse erinevasse logisse.  Seetõttu saab sündmusi otsida ka mitte logide, vaid hoopis sündmuse allika järgi.  Kõigepealt leiame olemasolevate sündmuste küljest allikad:

(Get-WinEvent -ListLog Application).ProviderNames

Get-WinEvent -ListProvider *
Get-WinEvent -ListProvider *PowerShell
Get-WinEvent -ListProvider Outlook

Nüüd teades allikat, on lihtne saada kätte sündmused:

Get-WinEvent -ProviderName Outlook -MaxEvents 10
Get-WinEvent -ProviderName Microsoft-Windows-PowerShell -MaxEvents 10

Teades täpsemalt mida otsime, saame ka sündmusi täpsemalt filtreerida.  Näiteks võime me vaadata ainult viimase 24 tunni jooksul toimunud sündmusi:

$eile = (Get-Date).AddDays(-1)
Get-WinEvent -FilterHashtable @{
  LogName = "Application"
  StartTime = $eile
}

Või siis otsime me kindlat sündmust, mille puhul on teada ka sündmuse number (EventID):

Get-WinEvent -MaxEvents 10 -FilterHashtable @{
  ProviderName = "Outlook"
  ID = 32
}

Get-WinEvent -FilterHashtable @{
  Logname="Application"
  ProviderName="Application Error"
  Data="iexplore.exe"
}

Kui me oleme eelnevalt rakenduses Event Viewer loonud sündmuste filtri, siis selle saab ette anda ka Powershellile.  Vaja see vaid salvestada XML failiks ja sealt võtta välja element QueryList.  Selle saab ka vaate filtrit muutes vahelehelt XML.

Get-WinEvent -MaxEvents 10 -FilterXml @"
<QueryList>
  <Query Id="0" Path="Application">
    <Select Path="Application">*[System[Provider[@Name='Application Error'] and (Level=2) and (EventID=1000)]]</Select>
  </Query>
</QueryList>
"@

Get-WinEvent -FilterXml ([xml](Get-Content .\filter.xml)) -MaxEvents 10

XML-dokumendiga tasub jännata ainult keerulisemate päringute puhul, mida varem näidatud viisidel ei saa kokku panna (näiteks, kui on vaja leida mitmele erinevale EventID-le vastavaid sündmusi vms.).

Logidesse kirjutamisega on asi natuke keerulisem.  Nimelt tuleks oma skripti jaoks luua uus logi või siis vähemalt uus sündmuste allikas.  Muidu on pärast raske oma skripti sündmusi üles leida.  Ja nii uute logide kui ka uute allikate loomiseks on vaja süsteemiülema õigusi:

$myProvider = "minuskript"

#Requires -RunAsAdministrator
New-EventLog -LogName Application -Source $myProvider
New-EventLog -Source TestApp -LogName TestLog

try {
  Get-WinEvent -ListProvider $myProvider | Out-Null
} catch {
  Start-Process -Verb runas powershell.exe -ArgumentList "New-EventLog -LogName Application -Source $myProvider"
}

Ülaltoodud tegevus tuleb sooritada ühekordselt.  Pärast seda on uus logi/allikas masinas defineeritud ja skript saab rahulikult kirjutada sündmusi:

$params = @{
  LogName = "Application"
  Source = $myProvider
  EventId = 1
  Message = "Minu kohandatud sündmus"
}
Write-EventLog @params

Get-WinEvent -ProviderName $myProvider