Doe coole dingen met Powershell

Ingediend door Dirk Hornstra op 27-may-2021 21:35

De wat oudere developers en computergebruikers weten het nog wel, als je jouw MS-DOS 6.2 computer opstartte, dan werd het bestand autoexec.bat geladen en de instellingen uit config.sys ingelezen. Daarmee kon je instellen dat je Soundblaster geluidskaart gebruikt werd en met DOS=HIGH,UMB werd DOS in het "hoge geheugen" geladen en had je meer geheugen beschikbaar om je spelletjes te starten. Als je wat wilde automatiseren, dan gebruikte je vaak batch-bestanden.

Dat zijn gewoon tekstbestanden. Dus je moest de syntax weten. En een foutje was makkelijk gemaakt.

Sinds een aantal jaar (volgens Wikipedia: 2006) is er Powershell. Je kunt in plaats van bestand.bat aan te maken nu een bestand.ps1 aanmaken. Die kun je in Powershell uitvoeren. Hiernaast kun je ook wat je in een bestand zet "los" in de Powershell uitvoeren. En omdat het "object-geörienteerd" is, heb je intelli-sense. Als je intypt: Get- en vervolgens een aantal keren op TAB drukt, dan zie je dat er een Get-Acl, Get-AdlAnalyticsAccount, Get-Alias en nog tig meer functies zijn.

Je kunt er zaken mee op je computer doen, qua bestanden, schijven, maar ook in IIS, met een Active Directory server. Ook zaken includen, zodat je C# functies kunt gebruiken. En via de functie Invoke-Command kun je het commando op een andere computer uit laten voeren. Als je het (nog) niet gebruikt, dan is dat eigenlijk zonde, want het heeft veel te bieden.

Volgens Wikipedia is het voor Windows (en dat was het in het begin ook), maar Microsoft gaat staat meer cross-platform ontwikkelen. Ook in de shell van Powershell krijg je de tip dat je kunt kijken naar de versie voor de verschillende besturingssystemen: https://aka.ms/pscore6, waar staat dat het dus ook werkt op Mac en Linux.

Voor veel dingen geldt: pas als je weet wat je ermee kunt ga je ermee aan de slag. Ik heb dus even gezocht op "powershell", ik had al via de blogs van Scott Hanselman de categorie op zijn website bekeken: 77 posts over "Powershell": link. Het duurt even voordat je naar dat item in de lijst springt, dat komt omdat er (te) veel artikelen geladen worden. Ook kom ik op de site van Hey, Script Guy! (link) een site waar vroeger veel VB zaken te vinden waren, maar waar nu dus ook Powershell een flink aandeel had. Nu staat daar dat deze site stopt, maar geen nood: op de Community site gaan we nu full pool voor Powershell: https://devblogs.microsoft.com/powershell-community/

Dus als je iets dagelijks nu handmatig doet (of regelmatig), denk er dan eens over na om er een powershell-script van te maken. Doe het 1x: kan handmatig. Doe het 2x: handmatig?, nou vooruit dan. Doe het 3x: nu moet je het toch automatiseren.

Bij mijn werk hebben we dit gebruikt voor de migratie van een flink aantal websites. Dat zijn dingen die je niet handmatig wilt doen. Je kopieert een site naar een andere server. Neemt daar alle eigenschappen over (eigen applicatie-pool, bindings, applicaties), verplaatst data in Active Directory, verplaatst de "oude site" naar een onderhoudspagina en stopt de "oude site". Vanaf de server waarop de site kom te draaien kun je alle acties uitvoeren, inclusief het stoppen op de "oude" server.

Met bovenstaande 3 linkjes zul je voldoende informatie moeten kunnen vinden, hierbij nog een aantal tips waar je hopelijk je voordeel mee kunt doen:

Gaat er wat fout, stop, ho!, niet doorgaan!

Je kunt fouten wegdrukken, maar dat moet je dus niet doen. Stel dat ik 50 websites over wil zetten en dat er bij de 19e halverwege iets fout gaat. Er missen zaken, dus de overgang is niet goed gegaan. Maar als je code door ratelt is de kans groot dat je het niet ziet en zul je op een later tijdstip "op de blaren zitten". Omdat er data mist, iets niet online komt. Het statement daarvoor, is dat je dit bovenin je .ps1-bestand zet:


$ErrorActionPreference = "Stop"

Invoke-Command, geef variabelen door

Bij het stoppen van de oude website, moet je een Invoke-Command actie uitvoeren op de andere server. Dat kan door het iis ID door te geven. Dat is een nummer en dat werkt op de volgende manier (met Using::variabelenaam kun je die doorgeven):


Invoke-Command -ComputerName NaamOudeServer  -ScriptBlock { Get-Website | Where-Object ID -eq $Using::iisID | Stop-Website }

Maar omdat we wat problemen hadden met bestanden die geblokkeerd bleven, kregen we de tip dat je ook de application-pool moet stoppen. Dat doe je op basis van de naam. Maar dat wilde niet werken, die variabele op dezelfde manier doorgeven als het iis ID (tekst in plaats van een nummer) werkt schijnbaar niet. Dus dan voeg je het als een soort variabele-aanroep door (als het niet linksom wil, dan maar rechtsom):


Invoke-Command -ComputerName NaamOudeServer -ArgumentList $applicationpoolNaam -ScriptBlock {param(string $applicationpoolNaam) Stop-WebAppPool -Name $applicationpoolNaam }

Gebruik Robocopy om bestanden te kopiëren

Dit is niet een specifiek Powershell punt (je kunt Robocopy overal gebruiken) maar meer de aanbeveling: wil je bestanden en/of mappen kopiëren, dan is Robocopy de enige super-stabiele en betrouwbare tool:


Robocopy.exe $bronPad $doelPad /MIR /COPY:DATSO /R:3 /W:10 /NP /SL /MT

Soms zijn bestanden gelockt en heb je het idee "de boel hangt/er gebeurt niets". Dat is niet het geval, de tool doet echt zijn werk wel. Maar vertrouw je het niet, voeg dan parameter /v toe. Dat is verbose: je krijgt output. Maar doe dat alleen om even een situatie te controleren en haal de output dan gauw weer weg. Die output vertraagt namelijk de kopieer-actie. Zonder output en het vergelijken van 10.000 gelijke bestanden (en 5 afwijkende), dat kan zonder output binnen de minuut, als alle 10.005 bestanden naar je scherm geschreven moeten worden, vul dan eerst je thermoskan dan maar met koffie...

Gebruik .NET objecten. Yes!

De .NET code-bibliotheken bevatten veel zaken waar je gebruik van kunt maken. Hier een aantal voorbeelden welke ik gebruik:


// encryptie / decryptie

$aesManaged = New-Object "System.Security.Cryptography.AesManaged"
$aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CBC
$aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::Zeros

// tekst omzetten naar bytes
$bytes = [System.Text.Encoding]::UTF8.GetBytes("tekstwaarde")

// een base-64 string omzetten naar bytes
$bytes = [System.Convert]::FromBase64String("tekst")

// "snelle" SQL connectie:
$conn = New-Object System.Data.SqlClient.SQLConnection
$conn.ConnectionString = "connectiestring"
$conn.Open()

$comm = $conn.CreateCommand()
$comm.CommandText = "SELECT * FROM tabel WHERE iets=@waarde"
$comm.Parameters.AddWithValue("@waarde", "1")

$dataset = New-Object System.Data.DataSet
$adapter = New-Object System.Data.SqlClient.SqlDataAdapter $comm
$adapter.Fill($dataset) | Out-Null
$conn.Close()

En zo zijn er nog heel veel commando's. Om nog even terug te gaan naar het begin, er is een statement wat in een batchbestand werkte en ook in een powershellbestand. En dat is:

pause

Hiermee pauzeer je en pas als je op een toets gedrukt hebt (Enter) ga je door naar de volgende stap. Is nog wel eens handig als je wilt controleren wat de output is en het anders met 130 kilometer per uur over je scherm heen vliegt :)