Drücke "Enter", um den Text zu überspringen.

Schlagwort: PowerShell

Windows 10: VPN-Verbindungen nutzen immer Remote-Gateway

Unter früheren Windows-Versionen konnte man bei VPN-Verbindungen noch den gesamten IPv4-Teil konfigurieren. Dort gab es eine Option “Standardgateway für das Remotenetzwerk verwenden”, die man abschalten konnte:

43e8230241a19524c77c2973e1ce137b

Standardmäßig war/ist diese Option eingeschaltet und sorgt dafür, dass SÄMTLICHER Datenverkehr über das Remote-Netz geleitet wird, auch das, was eigentlich “DIREKT” ins Internet gehen könnte. Der Vorteil dabei ist, dass der Traffic vom eigenen Endgerät bis zum Firmennetzwerk verschlüsselt ist, was insbesondere dann nützlich ist, wenn man in einem Internetcafe oder Hotel-WLAN sitzt und nicht klar ist, wer den Verkehr mithören kann.

Will man nun auf einem Windows 10 Client dieses Verhalten ändern – also erreichen, dass nur Traffic, der für das Remote-Netzwerk gedacht ist, über VPN geht, der Rest direkt ins Internet, müsste man diesen Haken entfernen, was bei Windows 10 nicht mehr geht, da sich die IPv4-Optionen der VPN-Verbindung nicht mehr öffnen lassen.

Was man aber tun kann, ist dieses Verhalten mit Hilfe der PowerShell herbeizuführen, und zwar mit diesem Aufruf:

123

Damit wird für alle VPN-Verbindungen das “Split-Tunneling” aktiviert, was eben diesem Haken entspricht.

Hier der Aufruf nochmal zum direkt Kopieren:

1
 Get-VpnConnection | Set-VpnConnection -SplitTunneling $True

456

Schreibe einen Kommentar...

Microsoft DNS-Server: Einträge massenweise “umziehen”

Wenn man mit einem Webserver zu einem anderen Provider wechselt oder aus sonstigen Gründen neue (öffentliche) IP Adressen zugewiesen bekommt, dann müssen natürlich auch die entsprechenden DNS-Einträge geändert werden. Wenn es sich hierbei nur um eine Hand voll handelt, könnte man dies auch manuell machen. Ab einer gewissen Menge ist das einfach nicht mehr praktikabel, da es a) zu lange dauert und b) auch sehr fehleranfällig wäre.

Als sinnvolle Lösung bietet sich hier die PowerShell an – vorausgesetzt, man hat einen Microsoft Windows Server als DNS-Server und auf diesem das entsprechende DNSServer Modul für die PowerShell verfügbar.

Das Skript, welches am Ende des Beitrages zum Download angeboten wird, sieht so aus:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<#
.Synopsis
   This script changes DNS A Records according to a CSV file - use at your own risk!
.DESCRIPTION
   This script changes DNS A Records according to a CSV file.
 
   The csv file should look like this:
 
   "OldIP","NewIP"
    "1.2.3.4","5.6.7.8"
    "2.4.6.8","1.3.5.7"
 
    to change all DNS records with "1.2.3.4" as value for "5.6.7.8" and "2.4.6.8" for "1.3.5.7"
 
.EXAMPLE
   Replace-DNSServerRecords.ps1 -CSVFile H:\OldAndNewIps.txt
 
   Replaces the DNS A records on the localhost DNS server according to the CSV-File H:\OldAndNewIps.txt
.EXAMPLE
   Replace-DNSServerRecords.ps1 -CSVFile H:\OldAndNewIps.txt -DNSServer mydns.domain.local -Verbose
 
   Replaces the DNS A records on the DNS server "mydns.domain.local" according to the CSV-File H:\OldAndNewIps.txt and writes verbose output
#>
 
Param(
    [Parameter(Mandatory=$True)]
    [ValidateScript({Test-Path $_ -PathType 'Leaf'})]
    [string]$CSVFile,
    [string]$DNSServer = "localhost"
)
 
Write-Warning "This script will possibly change a lot of DNS records. If you wish to cancel, press [CTRL]+[C], otherwise press [Enter]!"
Read-Host
 
# just in case of old PowerShell
Import-Module DNSServer
 
$CSVData = Import-CSV $CSVFile
 
$OldDNSRecordData = $CSVData.OldIP
$NewDNSRecordData = $CSVData.NewIP
 
$AllDNSZones = (Get-DnsServerZone -ComputerName $DNSServer | Where-Object IsReverseLookupZone -eq $False).ZoneName
 
$TotalChanges = 0
 
ForEach($CurrentDNSZone in $AllDNSZones)
{
 
    Write-Verbose "Processing current zone $CurrentDNSZone..."
 
    $AllDNSARecordsInZone = Get-DnsServerResourceRecord -ZoneName $CurrentDNSZone -RRType A -ComputerName $DNSServer
    Write-Verbose "Found $(($AllDNSARecordsInZone | Measure-Object).Count) DNS Records in current Zone"    
 
    ForEach ($CurrentRecord in $AllDNSARecordsInZone) {
 
        for($i=0; $i -lt $OldDNSRecordData.Length; $i++){
 
            If($CurrentRecord.RecordData.IPv4Address -eq $OldDNSRecordData[$i]) {
 
                Write-Verbose "Found an old DNS Record:"
                Write-Verbose "$($CurrentRecord.HostName) with $($CurrentRecord.RecordData.IPv4Address) in Zone $CurrentDNSZone"
 
                $NewRecord = $CurrentRecord.Clone()
 
                $NewRecord.RecordData.IPv4Address = $NewDNSRecordData[$i]
 
                Set-DnsServerResourceRecord -OldInputObject $CurrentRecord `
                                            -NewInputObject $NewRecord `
                                            -ZoneName $CurrentDNSZone `
                                            -ComputerName $DNSServer
                $TotalChanges++
            }
        }
    }
}
 
Write-Host "Changed Records: $TotalChanges"

Das Skript durchläuft alle Forward-DNS-Zonen und prüft dabei nacheinander alle vorhandene A-Records gegen die Liste der zu ändernden Einträge. Wenn ein entsprechender Eintrag gefunden wird, wird für diesen die IP-Adresse gegen die neue ausgetauscht. Am Ende gibt das Skript aus, wieviele Einträge insgesamt geändert wurden. Aus Sicherheitsgründen ist nach dem Start noch einmal zu bestätigen, dass das Skript seine Arbeit verrichten soll.

Das fertige Skript kann hier heruntergeladen werden:

Replace-DNSServerRecords.zip

Die Benutzung geschieht auf eigene Gefahr!

Schreibe einen Kommentar...

Hyper-V Server: Tatsächlichen freien Speicher der VMs auswerten

Wenn man in einer Hyper-V VM einen Blick auf den belegten bzw. freien RAM wirft, dann wird man häufig feststellen, dass nicht mehr viel übrig ist – und zwar dann, wenn man “Dynamic Memory”, den “Dynamischen Arbeitsspeicher” aktiviert hat. Dieser sorgt dafür, dass eine VM die Menge an Arbeitsspeicher bekommt, die sie aktuell benötigt – zumindest, solange noch Speicher frei ist und sich die VM in den vom Admin gesteckten Grenzen bewegt. So kann man seit dem Windows Server 2012 bzw. seit Hyper-V 3.0 drei verschiedene Werte konfigurieren:

DynMem0

Es lässt sich neben dem Startwert (Wieviel RAM bekommt die VM, wenn sie eingeschaltet wird?) weiterhin festlegen, wieviel die VM mindestens haben muss (Sie kann nie weniger haben, als hier festgelegt ist) und welche Menge ihr maximal zur Verfügung stehen könnte (Vorausgesetzt, es ist genügend RAM vorhanden; hier lässt sich auch ein Wert festlegen, der über dem insgesamt vorhandenen RAM liegt).

Bei Bedarf kann der “Minimale RAM” auch unter dem “Startwert” liegen – dies ist vor allem dann sinnvoll, wenn die VM während des Starts und der Anfangszeit nach dem Boot viel Speicher benötigt, um z.B. Dienste und Anwendungen zu starten, dann im Alltagsbetrieb aber mit weniger auskommt.

Nehmen wir nun an, eine VM hat folgende Konfiguration:

  • Start: 2GB
  • Min: 512MB
  • Max: 3GB

Nun wird sie also beim Einschalten zunächst 2GB bekommen, nach dem erfolgreichen Start ihres Betriebssystems den tatsächlichen RAM-Bedarf an Hyper-V melden und Hyper-V weist der VM dann diesen Bedarf plus einen Sicherheitsaufschlag (den “Arbeitsspeicherpuffer”) zu. Dieser ist nötig, damit die VM nicht jedes weitere benötigte Megabyte speicher einzeln anfordern muss, sondern dies immer “paketweise” tun kann.

Benötigt die VM nun mehr, dann bekommt sie mehr – solange, bis entweder das Maximum der VM-Konfiguration erreicht oder der RAM des Host-Systems voll ist. Einem System im laufenden Betrieb mehr RAM zuzuweisen ist nicht so kompliziert und ging bereits lange vor dem Virtualisierungszeitalter (ja, da musste man dann noch echte Speicherriegel in den Server stecken!).

Spannender wird der Vorgang, einer VM Speicher wegzunehmen, z.B. wenn sie eben nach dem Starten weniger Speicher benötigt, als im Startwert festgelegt. Wirklich Speicher “wegnehmen” kann man nicht. Dies wird durch eine Technik namens “Balooning” gelöst. Wenn der VM nun z.B. 512MB RAM weggenommen werden sollen, dann wird stattdessen in der VM (bzw. in deren Speicherbereich) eine “Ballon aufgeblasen”, der diese 512MB RAM belegt – die VM glaubt also, der Speicher wäre weiterhin vorhanden, aber aktuell belegt. Der Hypervisor “weiss” nun, dass er diesen RAM anderweitig vergeben kann, da er ja nicht wirklich belegt ist. Soll die VM wieder mehr Speicher bekommen, wird der Ballon stückweise kleiner gemacht, bis er verschwindet.

DynMem1

(In der Abbildung sehen wir eine VM, die nach dem Start, der mit 2GB RAM durchgeführt wurde,
nur noch 661MB benötigt und inkl. Aufschlag 788MB RAM zugewiesen bekommen hat)

Soweit die Technik, die in der Praxis sehr gut funktioniert. Jedoch hat sie einen Haken: Die VM im obigen Beispiel, welche nach dem erfolgreichen Start einen RAM-Bedarf von bspw. 661MB an Hyper-V meldet und 20% “Aufschlag” bekommt, soll nun 788MB RAM zugewiesen bekommen (Also 1260MB weniger als beim Start). In der Realisierung sieht sie weiterhin ihre 2GB – davon aber einmal die 1260MB Differenz (den “Ballon”) plus den tatsächlichen RAM-Bedarf (also 661MB) belegt. In der Konsequenz sind als (z.B. im TaskManager) 1921MB (1,87GB) belegt – von 2048MB insgesamt – also nur noch ca. 6% frei!

DynMem2 Der Taskmanager innerhalb der VM zeigt etwa zur selben Zeit, zu der der vorherige Screenshot erzeugt wurde, einen RAM-Verbrauch von 1,8GB und freien Speicher in Höhe von 204MB. Das hier etwas mehr RAM frei ist als bei der Rechnung oben liegt daran, dass die VM etwas mehr Speicherbedarf an Hyper-V meldet als tatsächlich bereits belegt sind.

Eine entsprechende Serverüberwachung, die es nicht besser weiss, meldet nun also, dass der RAM zu Neige geht. Das Fatale dabei ist aber, dass wenn man dieser VM z.B. einen größeren Startwert gibt, bspw. 3GB, dann wird die Rechnung noch schlimmer:

  • 3072MB beim Start
  • Nach dem Start 661MB belegt, 788MB zugewiesen
  • Ballon iHv 2284MB
  • Als belegt zu sehender Speicher: 2945MB (Ballon + 661MB tatsächlicher Bedarf)
  • Übrig bleiben dann 127MB (Der Puffer) – was hier nur noch etwa 4% (statt 6% bei 2GB Start-RAM) entspricht!

Dies geschieht, obwohl die VM augenscheinlich mehr RAM hat (oder zumindest haben KÖNNTE) und immer noch den selben Bedarf (von 661MB) hat! Hier darf man sich also nicht täuschen lassen.

Als Lösung könnte man den RAM-Verbrauch der VMs mittels PowerShell analysieren und dann bspw. bei Unterschreitung eines Schwellwertes bzgl. des freien RAMs alamieren. Ein solcher Aufruf, der den freien RAM aller VMs in Prozenten zeigt, könnte dabei so aussehen:

DynMem3

Zum Kopieren:

1
2
3
4
5
6
Get-VM | Where DynamicMemoryEnabled | Where State -eq "Running"
    Format-Table Name,
                 @{n='Benötigt(GB)';e={$_.MemoryDemand/1GB};FormatString='N3'},
                 @{n='Zugewiesen(GB)';e={$_.MemoryAssigned/1GB};FormatString='N3'},
                 @{n='Frei/Aktuell (%)';e={100-($_.MemoryDemand/$_.MemoryAssigned*100)};FormatString='N2'},
                 @{n='Frei/Max (%)';e={100-($_.MemoryDemand/$_.MemoryMaximum*100)};FormatString='N2'} -AutoSize

Die Spalte “Frei/Aktuell” liefert einen Wert, wieviel Speicher bezogen auf den aktuell zugewiesenen Wert frei ist (dieser Wert sollte sich in etwa in der Größe des Speicherpuffers bewegen, solange genügend RAM verfügbar ist und die VM mehr RAM als das Minimum benötigt).

Die letzte Spalte “Frei/Max” zeigt, wieviel Speicher bezogen auf den maximal möglichen RAM der VM noch frei ist. Erst wenn dieser Wert zu niedrig wird (bspw. unter 20% fällt) besteht Bedarf, der VM mehr RAM zuzuweisen.

Insgesamt sehen dann die Werte in der PowerShell, dem Taskmanager der VM und dem Hyper-V Manager so aus:

DynMem4 (Anklicken zum Vergrößern)

Schreibe einen Kommentar...

Windows Server 2012 R2: WSUS automatisch bereinigen

Mit der Zeit sammeln sich auf einem WSUS (Windows Server Update Service) einige Updates an. Da können auch schnell mehrere hundert Gigabyte an Daten zusammenkommen. Nicht jedes Update, welches auf dem WSUS gespeichert ist, wird aber noch benötigt. Daher ist es aus Gründen der Speicherplatzeffizienz sinnvoll, von Zeit zu Zeit etwas aufzuräumen. Dafür gibt es schon seit längerem einen passenden Assistenten in der WSUS-Konsole:

wsus_cleanup_00

Dieser Assistent ließ sich “früher” (also z.B. unter Windows Server 2008 R2, WSUS 3.0 SP2) nur manuell oder über komplizierte Skripte ausführen.

wsus_cleanup_01

wsus_cleanup_02

Seit Windows Server 2012 lässt sich WSUS aber auch über PowerShell steuern. Hier gibt es ein passendes Commandlet “Invoke-WsusServerCleanup”, welches die Bereinigung durchführt. Als Parameter kann verwendet werden:

Invoke-WsusServerCleanup [-CleanupObsoleteComputers] [-CleanupObsoleteUpdates]

[-CleanupUnneededContentFiles] [-CompressUpdates] [-DeclineExpiredUpdates]

[-DeclineSupersededUpdates] [-UpdateServer <IUpdateServer> ] [-Confirm] [-WhatIf]

 

Damit lässt sich unkompliziert steuern, welche Komponenten beräumt werden sollen.

wsus_cleanup_03

Dieses PowerShell-Commandlet kann nun z.B. im Rahmen eines kleinen Skriptes regelmäßig und automatisch (z.B. per Aufgabenplanung) ausgeführt werden. Damit spart man sich die regelmäßige, manuelle (aufwändige) Bereinigung mit Hilfe des Assistenten.

Die komplette Syntax zum angesprochenen PowerShell-Commandlet findet sich hier: http://technet.microsoft.com/en-us/library/hh826162.aspx 

Schreibe einen Kommentar...

SCCM 2012 R2: Neue Anwendungsanforderungen automatisch melden

Der System Center Configuration Manager (SCCM) 2012 R2 bietet die Möglichkeit, Anwendungen für Benutzer als “verfügbar” bereitzustellen. In dieser Kombination (und nur dort) lässt sich auch eine Genehmigungsanforderung einschalten:

approv1

Der Benutzer hat nun die Möglichkeit, die Software über den Application Catalog (Anwendungskatalog) anzufordern:

approv2

Wurde die Anforderung vom Benutzer ausgelöst, so taucht sie dann in der SCCM-Konsole auf:

approv3

Leider ist es nicht vorgesehen, dass man das Eintreffen einer neuen Anforderung per E-Mail o.ä. meldet und in der Regel sitzt kein Admin den ganzen Tag vor der GUI und wartet auf neue Anforderungen. Also muss man eine andere Lösung schaffen, dies weitgehend zu automatisieren.

Eine Variante wäre, bei Eintreffen eben eine E-Mail zu versenden. Dazu muss man das Eintreffen einer Anforderung automatisiert feststellen können. Und dazu ist die PowerShell sehr gut geeignet:

approv4

Der Aufruf dazu lautet:

Get-CMApprovalRequest | Where-Object {$_.CurrentState -eq 1}

(CurrentState ist der Zustand der Anfroderung; “1” bedeutet, sie ist neu und unbearbeitet, “4” bedeutet z.B., sie ist bereits genehmigt)

Mittels Format-Table o.ä. könnte man die Ausgabe noch aufbereiten:

approv5

Nun lässt sich diese Ausgabe z.B. in eine E-Mail verpacken. Ein komplettes Skript könnte dann so aussehen:

1
2
3
4
5
6
7
8
9
10
$FromAdr = admin@abc.de
$ToAdr = receiver@abc.de
$SMTPSrv = send.abc.de
$MailSubject = "New SCCM Application Approval Request"
 
If((Get-CMApprovalRequest | Where-Object {$_.CurrentState -eq 1} | Measure-Object).Count -gt 0)
{
    $Mailtext = Get-CMApprovalRequest | Where-Object {$_.CurrentState -eq 1} | ft Application,User,Comments -Auto
    Send-MailMessage -from $FromAdr -to $ToAdr -subject $MailSubject -body $Mailtext -smtpServer $SMTPSrv
}
1 Kommentar

PowerShell: Anmelde-Konto der Windows-Dienste überprüfen

Wenn auf einem Windows Server Dienste nicht mit dem richtigen Konto gestartet werden, können diverse Fehler auftreten, z.B. der Fehler 1079:

pwservices1

Der Fehler entsteht, wenn mehrere Konten unter dem selben Prozess (z.B. svchost) laufen, dabei aber verschiedene Konten nutzen sollen.

Nun muss man also herausfinden, welche Dienste betroffen sind. Dies geht sicherlich auch über die services.msc (also in der GUI) – ist dann aber mit viel Arbeit verbunden. Einfacher wäre es sicherlich, dies über PowerShell herauszufinden.

Leider kennt das Cmdlet “Get-Service” keine Möglichkeit, die Logon-Werte auszugeben:

pwservices2

Selbst der Aufruf “Get-Service | fl *” zeigt kein passendes Attribut:

pwservices3

Was bleibt nun also? Eine Abfrage mittels WMI!

pwservices4

Und tatsächlich – hier gibt es nun ein Attribut “StartName”, welches das verwendete Konto enthält. Nun kann man also eine einfache Liste aller Dienste mit ihren Konten abrufen:

pwservices5

Will man statt den “internen” (teil kryptischen) Dienstnamen die sprechenden Namen sehen, und auch nach diesen sortieren, dann kann man folgenden Aufruf verwenden:

Get-WmiObject win32_service | Sort-Object Caption | ft Caption,StartName

pwservices45

Über Where-Object kann man nun auch gezielt nach Diensten mit einem bestimmten Konto suchen:

pwservices7

Schreibe einen Kommentar...

Windows Server 2012 R2: Netzwerk-Profil mit PowerShell von “Öffentlich” auf “Privat” ändern

Oft kommt es vor, dass ein Windows Server das Netzwerk-Profil (Domäne oder Privat) nicht sauber erkennt und stattdessen auf “Öffentlich” steht. Dies hat natürlich Auswirkungen, z.B. auf die gesetzten Firewall-Regeln:

fw0

Eine kurze Überprüfung im “Netzwerk- und Freigabecenter” fördert das gleiche Ergebnis zu Tage:

fw1

Auch mit Hilfe der Windows PowerShell kann man dies sehen…

fw2

… und ändern!

fw3

Mit Hilfe des Aufrufs

Set-NetConnectionProfile –InterfaceIndex # –NetworkCategory Private

wird das Verbindungsprofil auf “Privat” gesetzt (“Domain” setzt auf Domäne). Nun kann man auch im Netzwerk- und Freigabecenter das korrekte Verbindungsprofil sehen:

fw4

3 Comments

SCCM: PowerShell-Skript, um OSD-Ergebnisse zu parsen

Wenn man im SCCM 2012 (R2) eine TaskSequenz laufen lässt, bricht diese normalerweise ab, sobald ein einzelner Step fehlerhaft ist. Um aber gerade bei längeren Tasksequenzen einen Abbruch (evtl. kurz vor Fertigstellung) zu vermeiden, kann man einzelne (oder auch alle) Schritte so konfigurieren, dass bei deren Fehler dennoch regulär weitergearbeitet wird:

TaskSequenz_BeiFehler

Wenn man nun diese Option wählt, kann es einem leicht passieren, dass man bei Fertigstellung einer Tasksequenz nicht sofort sieht, ob alle Schritte erfolgreich abgearbeitet wurden oder eben einzelne Schritte einen Fehler verursacht haben. Um diese leichter abprüfen zu können, habe ich ein kleines PowerShell-Skript geschrieben. Dieses erhebt nicht den Anspruch, aus Programmierer-Sicht optimal geschrieben zu sein, sondern es soll in erster Linie funktionieren. Und das tut es (zumindest in unserer Produktiv-Umgebung) sehr gut 😉

Hier nun das Skript-Listing (und hier als .ps1 zum Download):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
cls
$failed = New-Object System.Collections.ArrayList
$success = New-Object System.Collections.ArrayList
$logfiles = New-Object System.Collections.ArrayList
$logdirs = New-Object System.Collections.ArrayList
$notfound = 0
$tsfailed = $false
 
$logdirs += 'C:\Windows\ccm\logs'
$logdirs += 'C:\Windows\ccm\logs\smstslog'
$logdirs += 'c:\_SMSTaskSequence\Logs\Smstslog\'
$logdirs += 'c:\windows\system32\ccm\logs\Smstslog\'
$logdirs += 'c:\windows\system32\ccm\logs\'
$logdirs += 'c:\windows\sysWOW64\ccm\logs\'
 
foreach($logdir in $logdirs)
{
    If (Test-Path $logdir)
    {
        $foundlogfiles = Get-ChildItem $logdir -Filter smsts*.log
        foreach($foundlogfile in $foundlogfiles)
        {
            $logfiles += $foundlogfile.FullName
        }
    }
}
 
foreach ($file in $logfiles)
{
    Write-Host 'Parsing Logile' $file
    $logzeilen = Select-String -Path $file -Pattern 'Failed to run the action:'
    foreach ($zeile in $logzeilen)
    {
        $zeile = $zeile.ToString()
        $pos1 = $zeile.IndexOf('Failed to run the action:')
        $pos2 = $zeile.IndexOf(')]LOG]!&gt;')
        if ($pos2 -le $pos1)
        {
            $pos2 = $zeile.Length
        }
        $Paket = $zeile.Substring($pos1+26,$pos2-($pos1+26))
        $failed += $Paket
        $notfound++
    }
}
 
foreach ($file in $logfiles)
{
    $logzeilen = Select-String -Path $file -Pattern 'Successfully completed the action'
    foreach ($zeile in $logzeilen)
    {
        $zeile = $zeile.ToString()
        $pos1 = $zeile.IndexOf('Successfully completed the action (')
        $pos2 = $zeile.IndexOf(') with the exit')
        $Paket = $zeile.Substring($pos1+35,$pos2-($pos1+35))
        $success += $Paket
    }
}
 
foreach ($file in $logfiles)
{
    $logzeilen = Select-String -Path $file -Pattern 'Execution of task sequence failed'
    if ($logzeilen.Count -gt 0)
    {
        $tsfailed = $true
    }
}
 
if ($tsfailed)
{
    Write-Host -Foregroundcolor Red 'The whole TaskSequence failed to run!'
}
 
if ($notfound -gt 0)
{
    Write-Host -ForegroundColor Yellow 'At least one action failed. TaskSequence possibly aborted!'
    Write-Host -ForegroundColor Red 'Failed actions:'
    foreach($pak in $failed)
    {
        Write-Host $pak
    }
}
 
Write-Host -ForegroundColor Green 'Successfully completed actions:'
foreach($pak in $success)
{
    Write-Host $pak
}
 
if ($notfound -gt 0)
{
    Write-Host -ForegroundColor Yellow 'At least one action failed. TaskSequence possibly aborted!'
}
 
Read-Host "Done - press any key to continue ..."

Ausgabe sieht dann in etwa so aus:

Output

Schreibe einen Kommentar...

PowerShell: Alle Windows-Updates automatisch entfernen

Das entfernen ALLER Windows-Updates von Hand ist sehr mühsam. Die PowerShell schafft hier abhilfe. Das folgende kleine Skript ermittelt alle installierten Windows-Updates und entfernt diese der Reihe nach:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$hotfixes = Get-WmiObject -Class Win32_QuickFixEngineering | select hotfixid
 
foreach($hotfix in $hotfixes)
{
    $KBNummer = $hotfix.HotfixId.Replace("KB", "")
    $Kommando = "wusa.exe /uninstall /kb:$KBNummer /quiet /log /norestart"
    Write-Host ("Removing update with command: " + $Kommando)
    Invoke-Expression $Kommando
    While (@(Get-Process wusa -ErrorAction SilentlyContinue).Count -ne 0)
    {
        Start-Sleep 1
        Write-Host "Waiting for update removal to finish ..."
    }
}
Schreibe einen Kommentar...