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

Schlagwort: PowerShell

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:

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

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

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:

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:

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
}

$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:

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:

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

Was bleibt nun also? Eine Abfrage mittels WMI!

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:

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

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

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:

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

Auch mit Hilfe der Windows PowerShell kann man dies sehen…

… und ändern!

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:

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:

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]!>')
        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 ..."

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]!>') 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:

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 ..."
    }
}

$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...