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.
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.
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ä:
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 $fields=@($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 $fields=@($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}"))
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.
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öä.
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ä.
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.
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.
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
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ä 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
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>