Powershell 5:n myötä myös ZIP-arkistoja voi käsitellä Powershellillä. Tätä aiemmin piti käyttää jos jonkinmoisia kikkoja arkistojen purkamiseen, mikä etenkin komentoriviltä työskenneltäessä oli erittäin ärsyttävää. Arkisto luodaan tähän tapaan:

PS> Compress-Archive -LiteralPath tap-windows6 -DestinationPath tap-windows6-9.22.3.zip

Yleensä ottaen kohdetiedoston nimeen ei tarvitse lisätä tiedostopäätettä (.zip). Tässä tapauksessa Compress-Archive CmdLet kuitenkin hämmentyy, jos sitä ei ole:

PS> Compress-Archive -LiteralPath tap-windows6 -DestinationPath tap-windows6-9.22.3
 Compress-Archive : .3 is not a supported archive file format. .zip is the only supported archive file format.
 --- snip ---

Syynä ovat tietenkin kohdetiedoston nimessä olevat pisteet. Compress-Archive CmdLetissä on dokumentoimaton ja jokseenkin ärsyttävä bugi tai ominaisuus: se ei lisää pisteellä alkavia piilohakemistoja arkistoon paitsi jos ne määritellään eksplisiittisesti lähdetiedostolistauksessa (-LiteralPath). Lisäksi kaikki lähdetiedostolistauksen tiedostot ja kansiot lisätään suoraan arkiston juureen, eikä niiden polkua kunnioiteta. Siis esimerkiksi

-LiteralPath kansio1, kansio2/.piilokansio

luo seuraavan hakemistorakenteen arkistoon:

kansio1
 .piilokansio

Jostain syystä piilotiedostot kuitenkin kopioidaan normaalisti. Käytännössä tämä tarkoittaa sitä, että esimerkiksi Git-repositoryjen pakkaaminen zip-arkistoon ei kerta kaikkiaan onnistu Compress-Archive CmdLetillä.

Arkiston purku suoraan työhakemistoon tapahtuu seuraavasti:

PS> Expand-Archive -LiteralPath tap-windows6-9.22.3.zip -DestinationPath .

Vaihtoehtoisesti arkiston sisällölle voidaan luoda erillinen hakemisto purun yhteydessä:

PS> Expand-Archive -LiteralPath tap-windows6-9.22.3.zip -DestinationPath tap-windows6-9.22.3

Erilliseen hakemistoon purkaminen on hyödyllistä silloin, kun arkiston sisältö on suoraan sen juurihakemistossa.

Toisinaan voi tulla tarve säilyttää jonkin kansion hakemistorakenne, mutta poistaa sieltä suuria määriä tiedostoja. Näin voi käydä esimerkiksi silloin, kun halutaan säästää käännösprosessin tulokset, esim. exe-tiedostot, mutta päästä eroon lähdekoodista ja käännöksen tuottamista väliaikaistiedostoista. Tämä onnistuu kätevästi Powershellin Where-Object CmdLetillä. Alla oletetaan, että komennot ajetaan kansiosta, jonka sisältö halutaan siistiä. Lähtötilanteessa kansiossa on 91 tiedostoa ja hakemistoa:

PS> Get-Childitem -Recurse|Measure-Object|Select Count|Format-List
 Count : 91

Seuraavaksi poistetaan ne ei-hakemistot (~tiedostot), joiden tiedostopääte ei ole .exe:

PS> Get-ChildItem -Recurse|Where-Object { $_.Name -notlike "*.exe" -and -not $_.PsIsContainer }|Remove-Item
 
 PS> Get-Childitem -Recurse|Measure-Object|Select Count|Format-List
 Count : 12

Näin päästiin eroon lähes kaikesta tarpeettomasta. Tässä tapauksessa käännösprosessi oli kuitenkin tuottanut väliaikaishakemistoja, joita ei haluttu säilyttää. Niistä päästään kuitenkin eroon helposti:

PS> Get-ChildItem -Recurse|Where-Object { $_.Name -eq "unwanted-directory-name" }|Remove-Item -Recurse
 
 PS> Get-Childitem -Recurse|Measure-Object|Select Count|Format-List
 Count : 9

Vaihtoehtoisesti nämä kaksi siistimiskomentoa voi kytkeä yhteen ryhmittelemällä ehdot luovasti:

PS> Get-ChildItem -Recurse|Where-Object { ($_.Name -notlike "*.exe" -and -not $_.PsIsContainer) -or ($_.Name -eq "unwanted-directory-name") }|Remove-Item -Recurse

Huomaa, että jälkimmäinen ehto täsmäisi myös samannimisiin tiedostoihin, mikäli niitä olisi olemassa.

Windowsissa on useita komentorivityökaluja asennettujen ajurien tietojen selvittämiseen. Usein törmää seuraavaan komentoon:

PS> Get-WmiObject Win32_PnPSignedDriver

Luokka Win32_PnpSignedDriver sisältää tiedot ajurien digitaalisista allekirjoituksista sekä lisätietoja, kuten ajurin version. Useimmissa tapauksissa tämä komento riittää, sillä Windows ei ole Windows Vistan jälkeen suostunut lataamaan kuin kaupallisin varmentein allekirjoitettuja ajureita ilman, että se on test-signing -tilassa. Windows 10:n mukana tuli vaatimus siitä, että ajuri sisältää Microsoftin digitaalisen allekirjoituksen. Windows Server 2016 vaatii myöskin Microsoftin allekirjoituksen, mutta sen saaminen ajurille vaatii lisäksi, että Windows Hardware Lab Kit -testit on läpäisty. Käytännössä kehitys- ja testikoneita lukuun ottamatta kaikkien ajurien voidaan siis olettaa olevan kaupallisin varmentein allekirjoitettuja.

Ajurien kehitysvaiheessa drivers allekirjoitetetaan kuitenkin testivarmentein. Näistä ajureista ei luoka Win32_PnPSignedDriver -luokan instansseja, joten tutkimiseen tarvitaan muita komentoja. Eräs tapa on on käyttää ensin driverquery.exe -komentoa ajurin Inf-tiedoston nimen selvittämiseen:

PS> driverquery.exe /si
 
 DeviceName InfName IsSigned Manufacturer
 ============ ======= ========= ============
 --- snip ---
 TAP-Windows Adapter V9 oem10.inf FALSE TAP-Windows Provider V9
 --- snip ---

Tuloksia voi suodattaa Select-String -Cmdletillä tai selata more-komennolle putkittamalla. Kun Inf-tiedoston nimi on tiedossa, voidaan ajurin tiedot selvittää Get-WindowsDriver -CmdLetillä:

PS> Get-WindowsDriver -online -driver "oem10.inf"
 
 Driver : oem10.inf
 OriginalFileName : C:WindowsSystem32DriverStoreFileRepositoryoemvista.inf_amd64_d6d1390c54a88be5oemvista.inf
 Inbox : False
 ClassName : Net
 ClassDescription : Network adapters
 ClassGuid : {4D36E972-E325-11CE-BFC1-08002BE10318}
 BootCritical : False
 ProviderName : TAP-Windows Provider V9
 Date : 7/26/2018 12:00:00 AM
 Version : 15.14.22.364
 ManufacturerName : TAP-Windows Provider V9
 HardwareDescription : TAP-Windows Adapter V9
 Architecture : x64
 HardwareId : roottap0901
 ServiceName : tap0901
 CompatibleIds :
 ExcludeIds :
 
 Driver : oem10.inf
 OriginalFileName : C:WindowsSystem32DriverStoreFileRepositoryoemvista.inf_amd64_d6d1390c54a88be5oemvista.inf
 Inbox : False
 ClassName : Net
 ClassDescription : Network adapters
 ClassGuid : {4D36E972-E325-11CE-BFC1-08002BE10318}
 BootCritical : False
 ProviderName : TAP-Windows Provider V9
 Date : 7/26/2018 12:00:00 AM
 Version : 15.14.22.364
 ManufacturerName : TAP-Windows Provider V9
 HardwareDescription : TAP-Windows Adapter V9
 Architecture : x64
 HardwareId : tap0901
 ServiceName : tap0901
 CompatibleIds :
 ExcludeIds :

Jos ajurin nimi tai sen osa on tiedossa, voidaan myös käyttää Get-WindowsDriver -CmdLetiä putkitettuna Where-Object -CmdLetille:

PS> Get-WindowsDriver -Online |Where-Object { $_.ProviderName -like "TAP*" }
 
 Driver : oem10.inf
 OriginalFileName : C:WindowsSystem32DriverStoreFileRepositoryoemvista.inf_amd64_d6d1390c54a88be5oemvista.inf
 Inbox : False
 ClassName : Net
 BootCritical : False
 ProviderName : TAP-Windows Provider V9
 Date : 7/26/2018 12:00:00 AM
 Version : 15.14.22.364
 
 Driver : oem3.inf
 OriginalFileName : C:WindowsSystem32DriverStoreFileRepositoryoemvista.inf_amd64_0e2d432c2a230e0boemvista.inf
 Inbox : False
 ClassName : Net
 BootCritical : False
 ProviderName : TAP-Windows Provider V9
 Date : 6/22/2018 12:00:00 AM
 Version : 14.58.4.896
 
 Driver : oem9.inf
 OriginalFileName : C:WindowsSystem32DriverStoreFileRepositoryoemvista.inf_amd64_a572b7f20c402d28oemvista.inf
 Inbox : False
 ClassName : Net
 BootCritical : False
 ProviderName : TAP-Windows Provider V9
 Date : 4/21/2016 12:00:00 AM
 Version : 9.0.0.21

Kuten yllä näkyy, komento näyttää myös vanhat (Driver Storessa olevat) ajuriversiot, ellei tulostetta filtteröidä Inf-tiedoston perusteella.

Kun unixeissa pitää etsiä neula tekstimuotoisen datan heinäsuovasta, käytetään yleensä grep-komentoa. Jos tekstirivi täsmää annettuun hakuparametriin tulostetaan se oletustulostevirtaan (stdout). Grepin kanssa voidaan käyttää hakuparametreina yksinkertaisten merkkijonojen lisäksi säännöllisiä lausekkeita ja käyttää vaikka mitä muita hienoja kikkoja.

Powershellissä ei (ilmeisesti) ole mitään näin hienoa työkalua, mutta onneksi tekstimassasta saa useimmiten seulottua esille oleellisen Select-Stringin merkkijonohaulla. Esimerkiksi Windowsissa on oletuksena palomuurisääntöjä niin valtavat määrät, että niiden kaikkien läpikäynti jonkin tietyn säännön löytämiseksi on hullun hommaa. Hommaa voi helpottaa Select-Stringillä:

PS> netsh advfirewall firewall show rule name="all"|Select-String ssh
 
 Rule Name: SSHD Port OpenSSH (chocolatey package: openssh)
 Rule Name: sshd

Select-Stringillä voidaan näyttää myös osumia edeltävät ja seuraavat rivit -Context -parametrilla:

$ netsh advfirewall firewall show rule name="all"|Select-String -Context 5 ssh
 
 > Rule Name: SSHD Port OpenSSH (chocolatey package: openssh)
 ----------------------------------------------------------------------
 Enabled: Yes
 Direction: In
 Profiles: Domain,Private,Public
 Grouping:
 LocalPort: 22
 RemotePort: Any
 Edge traversal: No
 Action: Allow
 
 > Rule Name: sshd
 ----------------------------------------------------------------------
 Enabled: Yes
 Direction: In
 Profiles: Domain,Private,Public

Edellinen Select-String -komento on siis käytännössä sama kuin Linuxissa grep -A 5 -B 5, jossa -A tarkoittaa "after" ja -B "before".

Unix-komentotulkeissa kuten Bashissa on ollut vuosikaudet varsin monipuoliset tavat palata komentotulkin historiasta löytyviin komentoihin. Nuoli ylös palaa komentohistoriassa taaksepäin ja nuoli alas vie eteenpäin. Koko historian näkee komennolla "history":

$ history
 1257 git remote -v
 1258 cd /tmp/
 --- snip ---
 2242 id -un 0
 2243 id -gn 0

Tämän voi yhdistää grepin kanssa:

$ history|grep remote
 1264 git remote -v
 --- snip --

Yksi kätevimmistä on haku historiasta: ensin painetaan Ctrl-R ja sen jälkeen syötetään hakusana (alla "gpg"):

(reverse-i-search)`gpg': gpg -v --verify openvpn-2.4.6.tar.xz.asc

Mikäli järkevän oloinen komento löytyy, ajetaan se Return-näppäimellä. Jos komentoa halutaan muokata ajamatta sitä, painetaan Tab. Jos halutaan etsiä muita osumia, painetaan Ctrl-R uudelleen. Ctrl-R on erityisen kätevä silloin, kun on rakennettu rakkaudella valtavan pitkä komentorivi, joka on liian kaukana komentotulkin historiassa nuolinäppäimillä löydettäväksi.

Myös Powershellissä on mahdollista käyttää nuolinäppäimiä Bashin tapaan. Myös history-komento löytyy:

PS> Get-History 
 
 Id CommandLine
 -- -----------
 
 1 cd $HOME
 2 Get-ChildItem -Recurse
 3 cd opt
 --- snip --- 
 
 PS> Get-History | Where-Object { $_.CommandLine -like "*HOME*" } 
 
 Id CommandLine
 -- -----------
 
 1 cd $HOME

Historiasta haku onnistuu myös kirjoittamalla # ja syöttämällä hakusana...

PS> #history

... ja painalla Tab:

PS> Get-History | Where-Object { $_.CommandLine -like "*HOME*" }

Tab-näppäimellä voi etsiä muita osumia ja Return-näppäimellä valitaan tämänhetkinen komentorivi.

Aiemmassa blogikirjoituksessa käsittelin custom faktojen luontia dynaamisesti. Tällä kertaa käsittelemme ulkoisten faktojen ("external facts") perusteita. Puppetlabsin oma dokumentaatio aiheesta on varsin laadukasta, kunhan sen vain malttaa lukea ajatuksen kanssa (toisin kuin allekirjoittanut).

Ulkoisilla faktoilla tarkoitetaan faktoja, joiden arvon Facter päättelee niiden tulosteesta ja joita ei tyypillisesti ole kirjoitettu Rubyllä vaan esim. Bashilla, Pythonilla tai PowerShellillä. Ne toimivat muutoin kuin "oikeat" faktat, mutta niissä ei voida viitata muihin faktoihin, eikä niitä voi rajata ("confine") toimimaan vain jossain käyttöjärjestelmässä.

Ulkoiset faktat kannattaa lisätä jonkin Puppet-moduulin facts.d -hakemistoon, jolloin Puppet löytää ne automaattisesti. Alla luodaan muutama ulkoinen, yksinkertainen Powershell-fakta, jotka lisätään moduuliin psfacts:

> New-Item -Type directory psfactsfacts.d

Sitten luodaan uusi tiedosto, psfactsfacts.dfileversions.ps1, tekstieditorilla (esim. notepad++):

$openvpn_version = (Get-Item 'C:Program FilesOpenVPNbinopenvpn.exe').VersionInfo.ProductVersion
 
 $openssl_version = (Get-Item 'C:Program FilesOpenVPNbinlibssl-1_1-x64.dll').VersionInfo.ProductVersion
 
 Write-Output "openvpn_version=$openvpn_version"
 Write-Output "openssl_version=$openssl_version"

Fakta kaivaa ensin esiin Powershellillä OpenVPN:n (exe) ja OpenSSL:n (dll) versiot. Varsinaiset faktat luodaan tulostamalla ne Write-Output CmdLetillä muodossa

key=value

Huomaa, että samassa tiedostossa voidaan luoda useita faktoja: facteria kiinnostaa vain se, että skripti tulostaa jotakin tarkalleen yllä olevassa muodossa. Huomaa, että faktan nimen ja faktan arvon välillä ei saa olla välilyöntejä.

Faktaa on helpointa testata suoraan Powershellistä facterilla:

PS> facter.bat --external-dir psfactsfacts.d openvpn_version
 2.4.5.0
 PS> facter.bat --external-dir psfactsfacts.d openssl_version
 1.1.0f

Aivan samalla tavalla voidaan luoda external faktoja UNIX:ssa:

#!/bin/sh
 echo "fact=value"
 echo "anotherfact=value"

UNIX:in tapauksessa pitää muistaa määrittää komentotulkki (yllä /bin/sh) ja sallia tiedoston käynnistäminen (chmod +x <filename>).

Puppet on jo pitkään tukenut Windowsia. Monet natiivit Puppet-resurssit, kuten File, Package ja Service ovat jo pitkään toimineet sekä *NIX-käyttöjärjestelmissä että Windowsissa. Lisäksi osa natiiveista Puppet-resursseista, kuten Registry on suunniteltu pelkästään Windows-käyttöön.

Puppetin käyttömukavuus Windows-järjestelmien ylläpidossa on parantunut mutkan kautta muutamien uusien teknologioiden myötä:

  1. Powershell DSC
  2. Chocolatey
  3. Powershell Exec provider

Powershell DSC perustuu resurssien tilan hallintaan, aivan kuten Puppetkin. Mikä parasta, Puppet-koodissa voi käyttää natiivien Puppet-resurssien lisäksi Powershell DSC-resursseja. Puppetlabsin blogikirjoitus on hyvä alustus tähän aiheeseen. DSC vaatii toimiakseen Powershell 5.0:n, joka tulee Windows Management Framework 5.0:n mukana.

Chocolatey on pakettienhallintajärjestelmä sekä julkinen pakettivarasto Windowsille. Se perustuu Nuget-teknologiaan, jolla ohjelmistokehittäjät ovat perinteisesti asentaneet .NET-sovellusten kirjastoriippuvuuksia. Chocolatey siis laajentaa Nugetista, ohjelmistokehittäjien työkalusta, järjestelmäylläpitäjien työkalun. Käytännössä Chocolatey mahdollistaa Windows-sovelluksien asentamisen komentoriviltä samaan tapaan kuin apt-get Ubuntuissa ja Debianeissa. Hyödyllisimmillään Chocolatey on silloin, kun sitä käytetään Puppetista käsin niin sanottuna Package providerina.

Viimeisenä tulee Powershell Exec provider, jolla voidaan ajaa Powershell-skriptejä Puppetin natiivilla Exec-resurssilla.

Kaikkien kolmen käyttö Puppetissa on varsin yksinkertaista. Alla oletetaan, että Puppetserver on toimintakunnossa ja Windows-kone on liitetty siihen. Aluksi lisätään tarvittavat Puppet-moduulit Puppetserverin modules-hakemistoon:

$ git clone https://github.com/chocolatey/puppet-chocolatey chocolatey
 $ git clone https://github.com/puppetlabs/puppetlabs-dsc.git dsc
 $ git clone https://github.com/puppetlabs/puppetlabs-powershell powershell
 $ git clone https://github.com/Puppet-Finland/puppet-wmf wmf

Moduuleista viimeinen asentaa Windows Management Framework 5.0:n Chocolateyllä.

Tämän jälkeen Windows-koneen yaml-tiedostoon Hierassa lisätään luokka "wmf":

classes:
 - wmf

Seuraavan Puppet-ajon jälkeen Powershell DSC, Chocolatey Package provider ja Powershell Exec provider ovat valmiita käytettäväksi. Niiden toiminnan voi varmistaa luomalla yksinkertaisen Puppet-moduulin Puppetmasterin modules-kansioon:

$ cd <modules-dir>
 $ mkdir -p windowstest/manifests

Sitten luodaan tekstitiedosto dsctest/manifests/init.pp, jonka sisältö voi olla vaikka seuraavanlainen:

#
 class windowstest {
 
 dsc_file { 'test':
 dsc_ensure => 'present',
 dsc_destinationpath => 'C:dsc_testfile.txt',
 dsc_contents => 'foobar',
 }
 exec { 'testexec':
 provider => 'powershell',
 command => 'Add-Content -Path C:exec_testfile.txt
 -Value foobar',
 creates => 'C:exec_testfile.txt',
 }
 package { 'notepadplusplus':
 ensure => 'present',
 provider => 'chocolatey',
 }
 }

Sitten muokataan Windows-koneen yaml-tiedostoa Hierassa:

classes:
 - wmf
 - windowstest

Lopuksi Windows-koneelta voidaan tarkistaa onko Notepad++ asentunut ja onko Puppet luonut tekstitiedostot DSC:llä ja Powershell Exec providerilla:

> Test-Path 'C:Program FilesNotepad++'
 True
 > Get-Content C:dsc_testfile.txt
 foobar
 > Get-Content C:exec_testfile.txt
 foobar

Windowsissa voidaan käyttää hosts-tiedostoa ip-osoitteiden ja nimien liittämiseen toisiinsa, aivan kuten Linuxissakin. Linuxissa tiedoston sijainti on /etc/hosts, Windowsissa se on c:windowssystem32driversetchosts, tai oikeastaan $env:windirsystem32driversetchosts.

Hosts-tiedoston käyttäminen tulee ajankohtaiseksi silloin kun sovellus tarvitsee DNS-nimen, eikä DNS-palvelinta ole käytettävissä, tai sitä ei voida jostakin syystä käyttää.

Jos Puppet agentille ei ole määritetty palvelinta, se yrittää oletuksena yhteyttä palvelimeen jonka DNS-nimi on 'puppet', eli jos palvelimelle on määritetty domain 'example.com', agent hakee palvelinta jonka DNS-nimi on 'puppet.example.com' Määrittämällä server-asetuksella palvelimen DNS-nimi, voidaan Puppet agent osoittaa haluttuun palvelimeen. Tämä määritetään Linuxeissa tyypillisesti uusimmissa puppet-versioissa tiedostossa /etc/puppetlabs/puppet/puppet.conf osiossa main tai agent.

servername = puppet.example.com

Olemassa olevan Windowsin hosts-tiedoston muokkaaminen esim. Notepad-ohjelmassa sujuu yleensä ongelmitta. Hosts-tiedoston muokkaaminen tai rakentaminen Powershell-koodilla sen sijaan ei välttämättä suju. Muokkaamisen tai rakentamisen jälkeen saatetaan todeta että Puppet agent ei löydä palvelinta koska palvelimen oletusnimi (puppet) tai määritetty nimi eivät käänny ip-osoitteiksi. Seuraava koodi aiheuttaa tämänkaltaisen ongelman.

$hostsFile = "$env:windirSystem32driversetchosts"
 
 if (!(Test-Path "$hostsFile")) {
 New-Item -path $env:windirSystem32driversetc -name hosts -type "file"
 Write-Host "Created new hosts file"
 }
 
 # Remove any lines containing our puppetservername
 $tempfile = $env:temp + "" + (get-date -uformat %s)
 New-Item $tempfile -type file -Force | Out-Null
 Get-Content $HostsFile | Where-Object {$_ -notmatch "$puppetServerName"} | Set-Content $tempfile
 Move-Item -Path $tempfile -Destination $HostsFile -Force
 
 # Insert name, address of puppetserver separated by a tab 
 [email protected]($puppetServerAddress,$puppetServerName)
 $myString=[string]::join("`t", (0,1 | % {$fields[$_]}))
 $found = Get-Content $hostsFile | Where-Object { $_.Contains("$myString") }
 if ([string]::IsNullOrEmpty($found)) {
 [string]::join("`t", (0,1 | % {$fields[$_]})) | Out-File -encoding ASCII -append $hostsFile
 }

Eli ensin määritetään hosts-tiedoston sijainti. Jos tiedostoa ei ole olemassa, se luodaan. Seuraavaksi luodaan väliaikainen tiedosto, jonka sisältö haetaan olemassa olevasta hosts-tiedostosta, samalla poistaen nykyiset rivit jotka sisältävät palvelimen nimen (saatu parametrina). Muokattu tiedosto siirretään sitten oikeaan paikkaan. Tämän jälkeen tiedostoon lisätään palvelimen nimi ja ip-osoite (jotka saatu parametreinä).

Jos nyt tarkastellaan hosts-tiedostoa silmämääräisesti, se vaikuttaa olevan ok. Vaikka Puppet-palvelin on käytettävissä, agentin (ensimmäinen) ajo manuaalisesti tuottaa virheen.

Error: Could not request certificate: getaddrinfo: No such host is known.
 Exiting; failed to retrieve certificate and waitforcert is disabled

Samoin ping tuottaa virheen.

Ping request could not find host puppet.example.com. Please check the name and try again.

Tämä johtuu siitä että nimi puppet.example.com ei ratkea ip-osoitteeksi vaikka se on asianmukaisesti määritetty hosts-tiedostossa. Mistä on kyse?

Windows hosts-tiedoston ongelmat eivät ole aivan harvinaisia, ja näiden ongelmien etsiminen Googlella tuottaa paljon hakutuloksia, esim. tämän. Tarkistettavina asioina esitetään nimien resoluution välimuistia, enkoodausta, ylimääräisiä sarkaimia tai välilyöntejä jne.

Koska tässä artikkelissa on kyse Powershellistä, haluamme selvittää mistä ongelmassa on kyse, ja myös korjata ongelman Powershell-koodilla.

Ongelman syy on siinä että muokkaamalla hosts-tiedostoa edellä mainitulla Powershell-koodilla sen käyttöoikeudet, 'security descriptorit' jäävät vajaiksi. Muokkaamalla oikeudet asianmukaisiksi powershell-koodilla nimet ratkeavat oikein ip-osoitteiksi.

# Create access rule
 $userName = "Users"
 $rule = New-Object System.Security.AccessControl.FileSystemAccessRule("$userName", 'Read', 'Allow')
 
 # Apply access rule
 $acl = Get-ACL $hostsFile
 $acl.SetAccessRule($rule)
 Set-Acl -path $hostsFile -AclObject $acl

Annetaan siis ryhmälle 'Users' lukuoikeus tiedostoon. Tämä oikeus puuttuu, jos hosts-tiedostoa muokataan aiemman Powershell-koodin mukaan. Tämän lukuoikeuden antaminen riittää saattamaan nimiresoluution toimivaksi.

Tässä kokonainen toimiva funktio.

function SetupHostsFile {
 
 param(
 [IPADDRESS]$puppetServerAddress,
 [String]$puppetServerName
 )
 
 if ($debug) {
 write-host ("Now in function {0}." -f $MyInvocation.MyCommand)
 }
 
 
 If (-Not [BOOL]($puppetServerAddress -as [IPADDRESS])) {
 write-host ("{0} is not an IP address" -f $puppetServerAddress)
 break
 }
 
 $fqdnRe='(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{1,63}(?<!-).)+[a-zA-Z]{2,63}$)'
 
 If ($puppetServerName -notmatch $fqdnRe) {
 write-host ("{0} is not a fully qualified name" -f $puppetServerName)
 break
 }
 
 write-host "Setting up hosts file..."
 
 $hostsFile = "$env:windirSystem32driversetchosts"
 
 if (!(Test-Path "$hostsFile")) {
 New-Item -path $env:windirSystem32driversetc -name hosts -type "file"
 Write-Host "Created new hosts file"
 }
 
 # Remove any lines containing our puppetservername
 $tempfile = $env:temp + "" + (get-date -uformat %s)
 New-Item $tempfile -type file -Force | Out-Null
 Get-Content $HostsFile | Where-Object {$_ -notmatch "$puppetServerName"} | Set-Content $tempfile
 Move-Item -Path $tempfile -Destination $HostsFile -Force
 
 # Insert name, address of puppetserver separated by a tab 
 [email protected]($puppetServerAddress,$puppetServerName)
 $myString=[string]::join("`t", (0,1 | % {$fields[$_]}))
 $found = Get-Content $hostsFile | Where-Object { $_.Contains("$myString") }
 if ([string]::IsNullOrEmpty($found)) {
 [string]::join("`t", (0,1 | % {$fields[$_]})) | Out-File -encoding ASCII -append $hostsFile
 }
 
 # Create access rule
 $userName = "Users"
 $rule = New-Object System.Security.AccessControl.FileSystemAccessRule("$userName", 'Read', 'Allow')
 
 # Apply access rule
 $acl = Get-ACL $hostsFile
 $acl.SetAccessRule($rule)
 Set-Acl -path $hostsFile -AclObject $acl
 
 }

Voit lukea lisää Powershellin Set-Acl-cmdletistä täältä

Vagrantin shell-provisioner tukee suoraan Powershell-skriptejä. Toisin sanoen Windows-virtuaalikoneen asetuksia voi muokata Powershellillä ilman, että tarvitsee rakentaa uusi virtuaalikonekuva. Tyypillinen esimerkki on Windows liittäminen konfiguraationhallintajärjestelmään kuten Puppetiin.

Vagrantin dokumentaatiossa mainitaan, että Powershell-skriptille voi välittää parametreja powershell_args -valinnalla joko merkkijonona (string) tai listana (array). Esimerkkejä etenkin Powershell-provisioinnista on kuitenkin heikosti, mistä syystä tämäkin blogipostaus on kirjoitettu. Otetaan esimerkkinä skripti bootstrap_windows.ps1, joka liittää Windows-virtuaalikoneen olemassa olevaan Puppetserveriin. Sitä käytetään käsin Powershellistä seuraavasti:

> .bootstrap_windows.ps1 -certName win2012r2.local
 -ServerName puppet.local
 -puppetServerAddress 192.168.137.10

Rivinvaihdot on lisätty selvyyden vuoksi.

Jos bootstrap_windows.ps1 skripti halutaan ajaa suoraan Vagrantissa, tehdään se seuraavasti:

# -*- mode: ruby -*-
 # vi: set ft=ruby :
 
 Vagrant.configure("2") do |config|
 
 config.vm.define "win2012r2" do |box|
 box.vm.box = "mwrock/Windows2012R2"
 box.vm.box_version = "0.6.1"
 box.vm.hostname = "windows2012r2a"
 box.vm.network "private_network", ip: "192.168.31.101"
 box.vm.provider "virtualbox" do |vb|
 vb.gui = false
 vb.memory = 2048
 end
 box.vm.provision "shell" do |s|
 s.path = "bootstrap_windows.ps1"
 s.args = ["-certName", "win2012r2.local",
 "-ServerName", "puppet.local",
 "-puppetServerAddress", "192.168.31.1"]
 end
 end
 end

Huomaa, miten kukin parametri ja parametrin arvo on lisätty listaan erillisenä merkkijonona; syystä tai toisesta esimerkiksi "-certName win2012r2.local" ei toimi.

UNIX-tyyppissä järjestelmissä prosessitaulun tarkastelu on helppoa ps-komennolla. Samainen komento löytyy Powershellistäkin, mutta se on vain alias Get-Process CmdLetille, jolla saadaan helposti selville, mitä prosesseja järjestelmässä on käynnissä (alla typistetty tuloste):

> Get-Process
 
 Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
 ------- ------ ----- ----- ----- ------ -- -----------
 136 8 6428 8084 569 0.08 916 bash
 26 3 1496 2272 16 0.02 1560 cmd
 57 7 1096 5852 57 0.03 2144 firefox
 0 0 0 4 0 0 openvpn
 88 7 988 3668 50 0.02 2676 openvpnserv
 34 3 500 2332 14 0.00 4940 openvpnserv
 562 27 151992 161440 615 4.27 2388 powershell

Jos prosessin nimi on jo tiedossa, voidaan se antaa -Name -parametrilla - huomaa, että .exe jätetään pois lopusta:

> Get-Process -name "openvpn"
 
 Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
 ------- ------ ----- ----- ----- ------ -- -----------
 82 8 2376 6768 34 0.11 2228 openvpn

Hyödyntämällä Where-Object CmdLetiä voidaan valita prosesseja ilman, että määritellään niiden tarkka nimi:

> Get-Process | Where-Object { $_.Name -like "openv*" } 
 
 Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
 ------- ------ ----- ----- ----- ------ -- -----------
 82 8 2376 6768 34 0.11 2228 openvpn
 88 7 988 3668 50 0.02 2676 openvpnserv
 34 3 500 2332 14 0.00 4940 openvpnserv

Kuten yllä näkyy, Get-Process näyttää vain prosessin nimen, ei sen komentoriviparametreja. Nämä pitääkin kaivaa esille vaikeammin Get-WMIObject CmdLetillä:

> Get-WmiObject Win32_Process -Filter "Name = 'openvpn.exe'"|Select Commandline 
 
 Commandline
 -----------
 openvpn --service openvpn_exit_1 1 --config "community.ovpn"

Poistamalla -Filter -vivun näkyvät Commandline-attributiin lisäksi muutkin prosessin ominaisuudet.

Toisinaan tulee tarpeelliseksi etsiä yhdestä tai useammasta tiedostosta jotakin merkkijonoa ja tulostaa täsmäävä rivi sekä tiedosto, jossa se on, sekä rivinumero. GNU grepillä tämä onnistuu triviaalisti komennolla "grep -HN <pattern>":

$ grep -Hn REXML *.rb
 group_policy.rb:71: dFile = REXML::Document.new( File.new("#{dDir}\#{array[0][0]}.admx"))
 group_policy.rb:72: eFile = REXML::Document.new( File.new("#{eDir}\#{array[0][0]}.adml"))
 group_policy.rb:154: dFile = REXML::Document.new( File.new("#{dDir}\#{sys_tShort}.admx"))
 group_policy.rb:155: eFile = REXML::Document.new( File.new("#{eDir}\#{sys_tShort}.adml"))
 group_policy.rb:428: dFile = REXML::Document.new( File.new("#{dDir}\#{tShort}.admx"))
 group_policy.rb:429: eFile = REXML::Document.new( File.new("#{dDir}\#{adml}"))

Sama homma onnistuu myös Powershellissä, mutta hieman vaikeammin:

> Get-ChildItem . -include "*.rb" -Recurse|Select-String -Pattern "REXML"
 group_policy.rb:71: dFile = REXML::Document.new( File.new("#{dDir}\#{array[0][0]}.admx"))
 group_policy.rb:72: eFile = REXML::Document.new( File.new("#{eDir}\#{array[0][0]}.adml"))
 group_policy.rb:154: dFile = REXML::Document.new( File.new("#{dDir}\#{sys_tShort}.admx"))
 group_policy.rb:155: eFile = REXML::Document.new( File.new("#{eDir}\#{sys_tShort}.adml"))
 group_policy.rb:428: dFile = REXML::Document.new( File.new("#{dDir}\#{tShort}.admx"))
 group_policy.rb:429: eFile = REXML::Document.new( File.new("#{dDir}\#{adml}"))

Powershelliä UNIX-ylläpitäjille -artikkelisarjan muut osat:

Windowsin käynnistyviin ohjelma- ja kirjastotiedostoihin liittyy paljon erilaista metatietoa, esimerkiksi ohjelman versionumero, julkaisija yms. Näiden metatietojen tarkastelu on toisinaan hyödyllistä, etenkin ohjelmistoja testattaessa. Alla OpenVPN:n bin -kansion sisältö kokonaisuudessaan:

PS C:Program FilesOpenVPNbin> Get-ChildItem 
 
 Directory: C:Program FilesOpenVPNbin 
 
 Mode LastWriteTime Length Name
 ---- ------------- ------ ----
 -a--- 7.3.2016 16:40 1753440 libeay32.dll 
 -a--- 7.3.2016 16:40 226432 liblzo2-2.dll
 -a--- 7.3.2016 16:40 123000 libpkcs11-helper-1.dll 
 -a--- 7.3.2016 16:40 465760 openvpn-gui.exe
 -a--- 7.3.2016 16:40 965984 openvpn.exe 
 -a--- 7.3.2016 16:40 65888 openvpnserv.exe 
 -a--- 7.3.2016 16:40 396640 ssleay32.dll

Metatiedot on tallennettu tiedosto-objektin VersionInfo-parametriin - alla tarkastellaan openvpn.exe -tiedoston metatietoja:

PS C:Program FilesOpenVPNbin> (Get-ChildItem openvpn.exe).VersionInfo|fl 
 
 OriginalFilename : openvpn.exe 
 FileDescription : OpenVPN Daemon 
 ProductName : OpenVPN 
 Comments : 
 CompanyName : The OpenVPN Project 
 FileName : C:Program FilesOpenVPNbinopenvpn.exe 
 FileVersion : 2.3_guipr18and26.0 
 ProductVersion : 2.3_guipr18and26.0 
 IsDebug : False 
 IsPatched : False 
 IsPreRelease : False 
 IsPrivateBuild : False 
 IsSpecialBuild : False 
 Language : englanti (Yhdysvallat) 
 LegalCopyright : Copyright © The OpenVPN Project 
 LegalTrademarks : 
 PrivateBuild : 
 SpecialBuild : 

Yllä komento Get-ChildItem tarvitaan openvpn.exe:n muuntamiseksi Powershell-objektiksi.

Metatietojen lisäksi Powershellillä on mahdollista tarkistaa ohjelmien digitaaliset allekirjoitukset (Authenticoode signature). Tähän tarkoitukseen käytetään Get-AuthenticodeSignature CmdLetiä:

PS C:Program FilesOpenVPNbin> Get-AuthenticodeSignature *.exe 
 
 Directory: C:Program FilesOpenVPNbin 
 
 SignerCertificate Status Path 
 ----------------- ------ ----
 5E66E0CA2367757E800E65B770629026E131A7DC Valid openvpn-gui.exe 
 5E66E0CA2367757E800E65B770629026E131A7DC Valid openvpn.exe 
 5E66E0CA2367757E800E65B770629026E131A7DC Valid openvpnserv.exe

Digitaalisten allekirjoitusten kanssa Windows on nykyisellään kovin tarkka, joten tälle CmdLetille tulee varmasti tulevaisuudessa käyttöä.

Powershelliä UNIX-ylläpitäjille -artikkelisarjan muut osat:

Windowsin tuoteavain ("Product Key") löytyy esiasennetuissa koneissa usein koneen pohjasta, mutta entäpä jos Windows onkin jälkiasennettu, tai koneen pohjassa oleva tarra ei edustakaan todellisuutta? Jos tällainen kone soseutuu, ei tuoteavainta löydy tietenkään mistään fyysisestä paikasta, ja tuloksena on kone ilman Windowsia; tämä voi toki olla toivottavakin asiaintila, mutta lähdetään siitä oletuksesta, ettei näin ole.

Tuoteavain on onneksi tallennettu Windowsin rekisteriin, mistä sen saa otettua talteen useilla eri tavoilla. Koska tässä blogissa keskitytään automaatioon ja Powershellin, mitkään graafiset kikkareet eivät luonnollisestikaan tule kyseeseen. Onneksi homma hoituu myös alla olevalla Powershell-skriptillä (lähde):

Function Get-ProductKey { 
 $map="BCDFGHJKMPQRTVWXY2346789" 
 $value = (get-itemproperty "HKLM:SOFTWAREMicrosoftWindows NTCurrentVersion").digitalproductid[0x34..0x42] 
 $ProductKey = "" 
 for ($i = 24; $i -ge 0; $i--) { 
 $r = 0 
 for ($j = 14; $j -ge 0; $j--) { 
 $r = ($r * 256) -bxor $value[$j] 
 $value[$j] = [math]::Floor([double]($r/24)) 
 $r = $r % 24 
 } 
 $ProductKey = $map[$r] + $ProductKey 
 if (($i % 5) -eq 0 -and $i -ne 0) { 
 $ProductKey = "-" + $ProductKey 
 } 
 } 
 $ProductKey
 } 
 Get-ProductKey
 

Tämä skripti tallennetaan tiedostoon (esim. getproductkey.ps1) ja ajetaan Powershellissä. Komento tulostaa Windowsin tuoteavaimen, jonka voi pistää hyvään säilöön vastaisuuden varalle.

Powershelliä UNIX-ylläpitäjille -artikkelisarjan muut osat:

Toisinaan on tarpeen lisätä tekstitiedoston loppuun rivejä. UNIX:ssa tämä onnistuu helposti echo-komennolla:

$ echo matti >> vpn.pass
 $ echo salasana >> vpn.pass
 $ cat vpn.pass
 matti
 salasana

Sama onnistuu Powershellissä, mutta ei suinkaan Write-Host -komennolla, joka olisi echo-komennon lähin vastine, vaan Add-Content -komennolla:

> Add-Content vpn.pass -Value matti
 > Add-Content vpn.pass -Value salasana
 > Get-Content vpn.pass
 matti
 salasana

Toinen melko yleinen tarve on tiedostojen lataaminen verkosta suoraan komentoriviltä. UNIX:ssa tämä onnistuu helpoiten wget-komennolla:

$ wget http://build.openvpn.net/downloads/releases/openvpn-2.3.10.tar.gz

Vaihtoehtoisesti voidaan käyttää curl-komentoa, joka löytyy oletuksena lähes jakelusta kuin jakelusta:

$ curl -O http://build.openvpn.net/downloads/releases/openvpn-2.3.10.tar.gz

Powershellin uusimmissa versioissa on mukana Invoke-WebRequest -komento, jota voi käyttää samaan tarkoitukseen:

> Invoke-WebRequest -Uri http://build.openvpn.net/downloads/releases/openvpn-install-2.3.9-I601-x86_64.exe -OutFile openvpn-install-2.3.9-I601-x86_64.exe

Tämä on erittäin kätevä tapa noutaa esimerkiksi Windows Server 2012 r2:een jokin järkevä selain, esimerkiksi Mozilla Firefox; Internet Explorer on suoraan pakasta vedettynä käytännössä käyttökelvoton ylitiukkojen turva-asetusten takia. Joissain vanhemmissa Invoke-WebRequestin versioissa tiedostojen lataaminen ei onnistu sellaisista HTTPS-osoitteista, joiden webbipalvelimen varmenteeseen ei luoteta. Tähän ongelmaan löytyy onneksi kiertotie.

Lopuksi vielä vinkki, jolla voi luoda paikallisia Windows käyttäjiä ja liittää niitä ryhmiin:

> net user matti "password" /ADD
 > net localgroup administrators matti /add

Powershellissä itsessään ei vaikuta olevan käyttäjien luontiin omaa CmdLetiä.

Powershelliä UNIX-ylläpitäjille -artikkelisarjan muut osat:

Tietyn nimisten tiedostojen (tai tyyppisten) poistaminen hakemistosta on melko suoraviivaista GNU findilla:

$ ls
 README.txt stderr.log stdout.log
 $ find . -maxdepth 1 -type f -name "*.log" -exec rm -f {} +
 $ ls
 README.txt

Valinta "maxdepth 1" estää findia poistamasta tiedostoja mahdollisista alihakemistoista.

Toinen vaihtoehto on putkittaa tiedostolistaus xargsille:

$ ls
 README.txt stderr.log stdout.log
 $ ls *.log|xargs rm -f
 $ ls
 README.txt

Tässä ei kuitenkaan erotella toisistaan tiedostoja ja hakemistoja, joten myös mahdolliset .log -päätteiset hakemistot yrittäisiin poistaa siinä epäonnistuen.

Powershell-ratkaisussa yhdistetään molempia lähestymistapoja:

> Get-ChildItem . -File|Select Name
 
 Name
 ----
 stderr.log
 stdout.log
 README.txt
 
 > Get-ChildItem . -File -Filter "*.log"|Remove-Item
 
 > Get-ChildItem . -File|Select Name
 
 Name
 ----
 README.txt

Parametri -File näyttää hakemistosta vain tiedostot, ei hakemistoja. Jos haluttaisiin poistaa myös alihakemistoista mahdollisesti löytyvät tiedostot, pitäisi Get-ChildItemille antaa parametri -Recurse. Komento Select Name on mukana vain tulosteen selkeyttämiseksi.

Powershelliä UNIX-ylläpitäjille -artikkelisarjan muut osat:

UNIX-tyyppisissä käyttöjärjestelmissä on tulee usein tarve etsiä jostain hakemistosta lähiaikoina (alla minuutin sisällä) muuttuneet tiedostot:

$ find /var/log/ -mmin -1
 /var/log/debug
 /var/log/user.log
 /var/log/auth.log
 /var/log/syslog

Sama onnistuu myös Powershellissä ainakin kahdella tavalla, joista ensimmäinen on toiminnallisesti identtinen yllä olevan komennon kanssa:

PS C:UsersAdministratorDownloads> Get-ChildItem . -Recurse|Where-Object { $_.LastWriteTime -ge $(Get-Date).AddMinutes(-120) }|Select LastWriteTime, Name
 
 LastWriteTime Name 
 ------------- ----
 6/18/2015 install.bat

Yllä oleva komento käy läpi koko hakemiston rekursiivisesti tarkistaen jokaisen tiedoston kohdalla viimeisimmän kirjoitusajan ja verraten sitä Date-objektiin, jossa nykyisestä kellonajasta on vähennetty 120 minuuttia. Toisin sanoen komento näyttää ne tiedostot tai hakemistot, joita on muokattu viimeisen 120 minuutin sisällä. Selkeyden vuoksi tiedostojen tiedoista näytetään selkeyden vuoksi vain nimi ja kirjoitusaika.

Yllä oleva komentorivi ei todennäköisesti toimi optimaalisesti, sillä Get-Date -komento ajetaan jokaiselle tiedostolle erikseen - olisi parempi määrittää se muuttujana ja käyttää muuttujaa Where-Object -testissä (kuten täällä).

Toinen tapa etsiä viimeisimmät muuttuneet tiedostot perustuu tiedostojen järjestämiseen niiden muutosajan perusteella:

PS C:Program FilesOpenVPNlog> Get-ChildItem|Sort-Object LastWriteTime -Descending|Select -First 2|Select Name, LastWriteTime
 
 Name LastWriteTime
 ---- -------------
 community-stdout.log 6/18/2015 1:42:51 PM
 community-stderr.log 6/18/2015 1:42:51 PM

Tiedosto-objektit asetetaan muutosajan perusteella järjestykseen, jonka jälkeen luodusta listasta valitaan ensimmäiset kaksi. Tämä komento näyttää viimeksi muuttuneet tiedostot eikä ota kantaa siihen, onko muutokset tehty minuutti vai vuosi sitten.

Powershelliä UNIX-ylläpitäjille -artikkelisarjan muut osat:

Tulosteiden uudelleenohjaus ja paluuarvojen käsittely toimii lähes samoin *NIX-shelleissä ja Windowsin Powershellissä. Alla tarkistetaan Linuxin systemctl-komennolla olemassa olevan ("openvpn") ja olemattoman ("fake") palvelun tila, ohjataan sekä tuloste että virhetuloste bittitaivaaseen ja tulostetaan komennon paluuarvo:

$ systemctl status openvpn > /dev/null 2>&1
 $ echo $?
 0
 $ systemctl status fake > /dev/null 2>&1
 $ echo $?
 3

Kuten näkyy, paluuarvo on 0 jos palvelu on olemassa ja 3 jos sitä ei löydy.

Powershell toimii käytännössä identtisesti (alla käytössä NSSM):

> nssm.exe status openvpn > $null 2>&1
 > Write-Host $LastExitCode
 0
 > nssm.exe status fake > $null 2>&1
 > Write-Host $LastExitCode
 3

Myös Powershellissä voitaisiin käyttää muuttujaa $?, mutta se ei sisällä numeerista paluuarvoa vaan totuusarvon:

> nssm.exe status openvpn > $null 2>&1
 > Write-Host $?
 True
 > nssm.exe status fake > $null 2>&1
 > Write-Host $?
 False

Sekä *NIX-shelleissä että Powershellissä voisi tulostevirrat ohjata myös vaihtoehtoisella tavalla:

$ systemctl status openvpn > /dev/null 2> /dev/null
 > nssm.exe status openvpn > $null 2> $null

Powershelliä UNIX-ylläpitäjille -artikkelisarjan muut osat:

Windowsissa tekstimuotoisia lokitiedostoja UNIX:in tapaan ole, vaan niitä vastaa EventLog, jonka sisältöä voi tutkia kätevästi PowerShellillä. EventLogin lokit (Logs) voi listata seuraavasti:

PS C:> Get-EventLog -List 
 
 Max(K) Retain OverflowAction Entries Log 
 ------ ------ -------------- ------- --- 
 20 480 0 OverwriteAsNeeded 3 787 Application 
 20 480 0 OverwriteAsNeeded 0 HardwareEvents 
 512 7 OverwriteOlder 0 Internet Explorer 
 20 480 0 OverwriteAsNeeded 0 Key Management Service 
 20 480 0 OverwriteAsNeeded 2 606 Security 
 20 480 0 OverwriteAsNeeded 1 684 System 
 15 360 0 OverwriteAsNeeded 4 823 Windows PowerShell 

Halutun lokin (alla System) viimeisimmät kirjaukset (alla 10) saa näytettyä -Newest -vivulla:

PS C:> Get-EventLog System -Newest 10 
 
 Index Time EntryType Source InstanceID Message 
 ----- ---- --------- ------ ---------- ------- 
 1685 touko 14 1... Information Microsoft-Windows... 12 Prosessi C:WindowsSystem32svc... 
 1684 touko 14 1... Error NetJoin 4097 The machine windows81amd64 attem... 
 1683 touko 14 1... Error NetJoin 4097 The machine windows81amd64 attem... 
 1682 touko 14 1... Error NetJoin 4097 The machine windows81amd64 attem... 
 1681 touko 14 1... Error NetJoin 4097 The machine windows81amd64 attem... 
 1680 touko 14 1... Warning Microsoft-Windows... 219 The driver DriverWudfRd failed... 
 1679 touko 14 1... Information Microsoft-Windows... 10114 UMDF-ohjainliitin ei voinut käyn...
 1678 touko 14 1... Information Microsoft-Windows... 12 Prosessi C:WindowsSystem32svc...
 1677 touko 14 1... Error NetJoin 4097 The machine windows81amd64 attem...
 1676 touko 14 1... Warning Microsoft-Windows... 1014 Nimen win8.ipv6.microsoft.com. n...

Lokeista voidaan suodattaa näkyviin pelkästään halutun tyyppiset tapahtumat Where-Object CmdLetillä:

PS C:> Get-EventLog System -Newest 10|Where-Object { $_.Source -eq "NetJoin" }
 
 Index Time EntryType Source InstanceID Message
 ----- ---- --------- ------ ---------- -------
 1684 touko 14 1... Error NetJoin 4097 The machine windows81amd64 attem...
 1683 touko 14 1... Error NetJoin 4097 The machine windows81amd64 attem...
 1682 touko 14 1... Error NetJoin 4097 The machine windows81amd64 attem...
 1681 touko 14 1... Error NetJoin 4097 The machine windows81amd64 attem...
 1677 touko 14 1... Error NetJoin 4097 The machine windows81amd64 attem...

Lopuksi kiinnostavalta näyttävä lokikirjaus voidaan tulostaa järkevässä muodossa yhdistämällä Where-Object ja Format-List:

PS C:> Get-EventLog System|Where-Object { $_.Index -eq 1684 }|Format-List
 
 Index : 1684
 EntryType : Error
 InstanceId : 4097
 Message : The machine windows81amd64 attempted to join the domain SMB but failed. The error c
 ode was 1332.
 Category : (0)
 CategoryNumber : 0
 ReplacementStrings : {SMB, windows81amd64, 1332}
 Source : NetJoin
 TimeGenerated : 14.5.2015 16:11:37
 TimeWritten : 14.5.2015 16:11:37
 UserName : windows81amd64Heino

Tämän viimeisimmän komennon voisi toteuttaa korvata myös Get-WinEvent CmdLetillä, mutta senkin kanssa on käytettävä Where-Objectia, joten tehokkuus lienee samaa luokkaa kuin Get-EventLogia käytettäessä.

Powershelliä UNIX-ylläpitäjille -artikkelisarjan muut osat:

Powershelliä konffatessa on tullut taas törmättyä useisiin varsin käyttökelpoisiin komentoihin. Alla ei-kattava lista, sillä lisää on luvassa sopivalla hetkellä.

Hakemiston poistaminen rekursiivisesti:

$ rm -rf <hakemisto>
 > Remove-Item -Recurse -Path <hakemisto>

Tiedoston viimeisten rivien näyttäminen:

$ cat <tiedosto>|tail -n <rivien_määrä>
 > Get-Content <tiedosto> -Last <rivien_määrä>

Tiedoston ensimmäisten rivien näyttäminen

$ cat <tiedosto>|head -n <rivien_määrä>
 > Get-Content <tiedosto> -First <rivien_määrä>

Tiedoston rivien määrän laskeminen:

$ cat <tiedosto>|wc --lines
 > Get-Content <tiedosto>|Measure-Object -Line

Powershelliä UNIX-ylläpitäjille -artikkelisarjan muut osat:

Lisävinkkejä tuli keksittyä nopeammin kuin alunperin osasin uumoillakaan. Näille on ollut käyttöä jo useaan kertaan.

Prosessin ID:n löytäminen:

$ ps aux|grep <nimi>
 > Get-Process -Name <nimi>

Prosessin tappaminen PID:n perusteella

$ kill <pid>
 > Stop-Process -Id <pid>

Prosessin tappaminen nimen perusteella

$ pkill <nimi>
 > Stop-Process -Name <nimi>

Powershelliä UNIX-ylläpitäjille -artikkelisarjan muut osat:

menucross-circle