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

Kategorie: System Center

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

Windows Server und System Center Technical Previews stehen bereit

Seit dem 01. Oktober stehen nun neben der Technical Preview von Windows 10 auch die Previews für den kommenden Windows Server sowie für System Center bereit:

technicalpreviews

In 5 großen Bereichen soll es Änderungen beim Windows Server geben:

  • Infrastructure Upgrades
  • Networking
  • Storage
  • Remote Desktop
  • Identity and Access Management

Dies ist einem Blog-Artikel vom “Microsoft Server and Cloud Plattform Team” zu entnehmen.

Ein finaler Name für den neuen Windows Server steht noch nicht fest, aber ich vermute, dass er “Windows Server 2015” heißen wird. Über Details werde ich zeitnah berichten.

Den Download der Preview erhält man über die MSDN oder das Evaluation Center.

Schreibe einen Kommentar...

WDS und SCCM oder 2x WDS parallel betreiben / Probleme mit PXE lösen

Wenn man (z.B. während der Einführungsphase vom System Center Configuration Manager) den bisherigen WDS-Server (Windows Bereitstellungsdienste / Deployment Services) weiterhin nutzen will, aber parallel die Betriebssystembereitstellung (OSD) von SCCM benötigt, dann besteht im Wesentlichen das folgende Probleme:

Da PXE auf Broadcasts basiert, kann es nur einen PXE-Server geben, den der Client letztlich kontaktiert (man kann per Verzögerung dafür sorgen, das einer immer schneller ist als der andere). Wenn man nun also PXE am SCCM aktiviert, dann ist es quasi Glückssache, ob der Client zuerst die Meldung vom WDS oder zuerst die von SCCM empfängt – in den meisten Tests war SCCM schneller. Damit bleibt also nur eine der beiden Technologien nutzbar.

Aber es gibt eine Lösung! Diese ist leider a) nicht wirklich dokumentiert und b) seitens Microsoft auch nicht unterstützt (man hört aber, das selbst Microsoft diese Lösung intern einsetzen soll).

Die Lösung besteht darin, dem Benutzer am Client die Wahl zu lassen, welchen der gefundenen PXE-Server er nutzen will. Um dies zu erreichen, ist am WDS-Server (also derjenige, der nicht der SCCM-Server ist) ein Registry-Key zu setzen:

pxe1

Zusätzlich muss am SCCM in den eigenschaften des Distribution-Points (Verteilungspunkt) für eine ausreichende Verzögerung gesorgt werden (würde man zuerst den PXE vom SCCM booten, dann hat der RegKey dort keine Wirkung, da dieser nur auf den WDS-eigenen PXE-Provider wirkt, nicht aber auf den vom SCCM):

pxe5

Wenn nun ein Client einen PXE-Boot versucht (und die Verzögerung ausreichend war, dass sich zuerst der Nur-WDS-PXE-Server meldet), dann bekommt der Benutzer zusätzlich zu der Möglichkeit, per F12 vom Netzwerk zu booten eine weitere Option: F11 für eine Server-Auswahl!

pxe2

Drückt man jetzt F12, wird wie gewohnt DIESER WDS-Server genutzt und von dort mittels PXE gebootet. Drückt man jedoch F11, werden zuerst alle verfügbaren WDS-Server erkannt:

pxe3

Danach bekommt man eine Auswahl-Liste mit allen gefundenen PXE-Servern:

pxe4

Hier kann nun der jeweilige PXE-Server gewählt werden. Der WDS-Server selber steht an erster Stelle, an zweiter Stelle steht hier der SCCM mit aktiviertem PXE.

Auf diese Weise ist es möglich, WDS und SCCM oder mehrere WDS-Server parallel zu betreiben. Natürlich muss die entsprechende DHCP-Infrastruktur aufgebaut sein, damit PXE überhaupt funktionieren kann!

Schreibe einen Kommentar...

SCCM 2012: “Inboxes”-Verzeichnis füllt sich / Laufwerk läuft voll

Auf einem unserer SCCM-Server ist mir aufgefallen, dass sich das Laufwerk C:\, welches großzügig bemessen ist, nahezu komplett gefüllt hat. Eine einfach Analyse mit TreeSize brachte folgendes zum Vorschein:

inboxes1

Wie auf dem Screenshot zu sehen ist, hat das receive-Verzeichnis unter "C:\Program Files\Microsoft Configuration Manager\inboxes\despoolr.box\receive" eine beachtliche Größe bekommen. Aber was sind das nun für Dateien? Zuersteinmal kann man allgemein sagen, dass dies Daten sind, die auf dem lokalen Verteilungspunkt (“Distribution Point”, DP) verteilt werden sollten und hier während der Empfangs-Phase zwischengespeichert wurden. Aber um welches Paket handelt es sich?

Dazu kann man das Logfile bemühen. Unter “C:\Program Files\Microsoft Configuration Manager\Logs” liegen die verschiedenen Log-Dateien, für diesen Zweck zuständig ist die despool.log (oder auch despool.lo_, falls die ursprüngliche despool.log ihre maximale Größe erreich hatte). Öffnen kann man die Logdateien am besten mit dem SCCM-eigenen Tool “CMTrace”, welches unter "C:\Program Files\Microsoft Configuration Manager\tools\cmtrace.exe" zu finden ist. Dort kann man nun mittels des Fernglas-Symboles suchen:

inboxes2

Als Suchtext kann der Name der größeren Datei verwendet werden, hier also “PKGhl61p.TRY”. Dabei kommt dann auch der Paketname zum Vorschein:

inboxes3

Hier in diesem Fall also “CAS00045”. Nun kann man in der Configuration Manager Konsole unter “Überwachung / Verteilungsstatus / Inhaltsstatus” den Status für des betreffende Paket prüfen:

inboxes4

Da das Paket (mittlerweile) erfolgreich auf den lokalen DP kopiert wurde, kann man die *.TRY-Datei also löschen. Dass dies nicht automatisch vom System gemacht wurde, dürfte hier in diesem Fall daran liegen, dass die Verteilung über WAN auf Grund der Größe des Paketes mehrfach gescheitert ist…

Schreibe einen Kommentar...

SCCM 2012 R2: Timeout bei OSD-Fehlern von 15 Minuten auf beliebigen Wert erhöhen

Wenn es während einer Tasksequenz im System Center Configuration Manager 2012 R2 zu einem Fehler kommt, so wird die Fehlermeldung standardmäßig für 15 Minuten angezeigt – danach wird der Client neugestartet. Wenn man nun eine längere Tasksequenz laufen lässt, wird man selten die gesamte Zeit vor dem betroffenen Rechner verbringen und so auch die Fehlermeldung verpassen. Noch schlimmer wird es, wenn der Fehler noch vor dem Abschluss der Formatierung des Laufwerkes geschieht – denn bis zu diesem Punkt ist das Logfile lediglich in einer Ram-Disk abgelegt – und die ist beim Neustart natürlich weg!

Diese Fehlermeldungen sehen dann in etwa so aus:

SCCM_0x80070002

Dieses Verhalten lässt sich glücklicherweise abändern – mit einem nicht all zu hohem Aufwand! Dazu muss lediglich in der betreffenden Tasksequenz (es geht leider nicht pauschal) eine Tasksequenzvariable gesetzt werden.

Dazu wird die Tasksequenz geöffnet und direkt an erster Stelle ein weiterer Schritt “Tasksequenzvariable festlegen” eingefügt:

sccm_ts_var

Der Name der Variable lautet “SMSTSErrorDialogTimeout” – der Wert ist in Sekunden anzugeben:

sccm_ts_var2

Damit ist die gewünschte Änderung auch schon gemacht. Beim nächsten Start der Tasksequenz ist die gemachte Änderung auch schon wirksam… Und dann kann man im Falle eines Fehler mittels F8 die DOS-Box öffnen und beispielsweise mit “cmtrace.exe” die Logfiles analysieren.

1 Kommentar

SCCM 2012 R2: Logging während OSD verbessern

Während einer Betriebssysteminstallation (“OSD” – Operating System Deployment) werden alle Schritte protokolliert. Das Problem dabei ist, dass das Logfile dabei maximal 1MB groß werden darf. Alle älteren Einträge werden überschrieben. Und ein Megabyte ist selbst beim Standard-Loglevel nicht sehr viel…

Um die gewünschten Änderungen zu erreichen, müssen die Boot-Images (“boot.wim”) angepasst werden. Dabei muss eine Konfigurationsdatei erzeugt und in den Abbildern hinterlegt werden. Diesen Vorgang möchte ich hier etwas genauer beschreiben:

Als erstes besorgt man sich die aktuell verwendeten Startabbild-Dateien für 32 Bit und 64 Bit. Wo diese zu finden sind, kann man in den Eigenschaften der Abbilder nachsehen. ^

Screenshot (18)

Der übliche Speicherort lautet

\\[HOSTNAME]\SMS_[SITECODE]\OSD\boot\i386\

für 32 Bit und

\\[HOSTNAME]\SMS_[SITECODE]\OSD\boot\x64\

für 64 Bit.

Als zweites benötigt man das Windows ADK in einer passenden Version. Dieses kann bei Microsoft heruntergeladen werden. (http://www.microsoft.com/de-de/download/details.aspx?id=39982)

Nun kann mit Hilfe von DISM in einer Eingabeaufforderung mit administrativen Rechten die jeweilige WIM-Datei zum verändern gemountet werden. Dies geschieht mittels des Aufrufes

dism /Mount-Wim /WimFile:C:\boot.wim\boot.CAS00005.wim /index:1 /MountDir:C:\boot.wim\mount_x64

(Die Pfade sind entsprechend anzupassen)

Screenshot (13)

Nun kann im Windows-Pfad des gemounteten Images eine Datei mit Namen “SMSTS.INI” und folgendem Inhalt abgelegt werden:

[Logging]

LOGLEVEL=0

LOGMAXSIZE=10485760

LOGMAXHISTORY=3

DEBUGLOGGING=1

CCMDEBUGLOGGING=1

Das LogLevel 0 steht für “verbose”, ist also der höchste Detailgrad. Standard wäre 1…

LogMaxSize erklärt sich sicher von selbst – der Wert wird in Bytes angegeben (hier also 10MB)

LogMaxHistory sorgt dafür, dass nicht nur das letzte LogFile gelesen werden kann, sondern auch die vorherigen. Im Normalfall steht der Wert auf 1 und dabei werden ältere Logs immer durch das aktuelle überschrieben.

DebugLogging aktiviert das Protokollieren von Debug-Meldungen (1 ist “true”, 0 ist “false”)

CCMDebugLogging tut im wesentlichen das selbe, Microsoft empfiehlt die Verwendung beider Varianten, ich vermute aus Gründen der Abwärtskompatibilität

Screenshot (15) 

Die Namen der Konfigurationsparameter müssen in Großbuchstaben geschrieben werden!

Screenshot (14)

Nun kann die WIM-Datei wird zurückgeschrieben werden, dazu dient wieder DISM mit folgendem Kommando:

dism /Unmount-Wim /MountDir:C:\boot.wim\mount_x64 /commit

Screenshot (17)

Danach muss das WIM-File nur noch zurück auf den SCCM-Server kopiert werden und neu auf die Verteilungspunkte (“Distribution Points”) verteilt werden…

Schreibe einen Kommentar...

SCOM 2012 R2 – Auto-Defragmentierung

Wenn man den Operations Manager 2012 nutzt, dann wird man vermutlich auch schon einmal über eine Meldung gestolpert sein, die darauf hinweist, dass ein logisches Laufwerk zu stark fragmentiert ist („Fragmentierungsgrad des logischen Datenträgers ist hoch“ / „Logical Disk Fragmentation Level is High“). Wenn man viele Server überwacht, dann wird man diese Meldung auch sehr oft sehen – ganz speziell am Montag Morgen (das liegt daran, dass die Standardeinstellung dieses Monitor dafür sorgt, dass der Test immer Samstag 03:00 Uhr läuft).

Defrag1

Nun hat man im Wesentlichen 3 Optionen:

  1. Meldung hinnehmen und von Hand auflösen – bedeutet u.U. sehr viel Aufwand
  2. Meldung ignorieren oder gar mittels Override deaktivieren – löst aber nicht die Ursache auf
  3. Meldung nutzen, um nicht nur die Meldung selbst (=Wirkung), sondern auch den Zustand (=Ursache) selbst aufzulösen

Die Möglichkeit, die Defragmentierung zu automatisieren ist auch im Wissensdantenbank-Artikel des Monitors erwähnt:

Defrag2

Wie das genau geht, hat Cameron Fuller, MVP für SCOM, in seinem Blogartikel beschrieben:

http://tinyurl.com/OMAutodefrag

Ein großartiger Artikel!

Update: Es gibt auch einen etwas einfacheren Weg – man kann das selbe Ziel auch mit einem einfachen Override erreichen, somit ist auch kein weiteres MP nötig… Wie genau das funktioniert, werde ich zeitnah nachreichen.

Schreibe einen Kommentar...

SCVMM 2012 R2: Tastatur-Layout einer VM-Vorlage ändern

Das Problem

Wenn man im SCVMM 2012 R2 (gilt auch für “R1”) eine VM mit Hilfe einer VM-Vorlage (engl. “Template”) erzeugt, dann bekommt diese neue VM (und zwar unabhängig von dem bereits vorhandenen Betriebssystem in der eingesetzten VHD) alle Regionaleinstellungen auf “en-US”, dazu gehören u.a.:

  • Tastaturlayout
  • User-Locale
  • System-Locale
  • UILanguage

Die einzige Einstellung bezüglich der Region lässt sich für die Zeitzone einrichten:

scvmmregio1

Selbst wenn man mit Hilfe einer eigenen Antwortdatei andere Werte setzt, werden diese weitgehend ignoriert, da am Ende in der resultierenden Antwortdatei (Eigene Inhalte + im SCVMM gesetzte Einstellungen + SCVMM-Defaults) die SCVMM-Defaults am weitesten oben stehen und daher Vorrang bekommen.

Dieses Verhalten ist bei Microsoft bekannt und in dem folgenden KB-Artikel beschrieben:

http://support.microsoft.com/kb/2709539

Lösung des Problems

Als Workarround ist folgendes Vorgehen möglich:

  1. SCVMM-Konsole starten
  2. In den “Einstellungen”-Bereich wechseln
  3. Dort die PowerShell über den Button in der Ribbon-Leiste öffnen
  4. Ein angepasstes Script nach folgendem Schema ausführen

scvmmregio2

PowerShell-Script:

$Vorlage = Get-SCVMtemplate | where {$_.Name  -eq "Template_Name"}             
$Einstellungen = $Vorlage.UnattendSettings;            
$Einstellungen.add("oobeSystem/Microsoft-Windows-International-Core/UserLocale","de-DE");            
$Einstellungen.add("oobeSystem/Microsoft-Windows-International-Core/SystemLocale","de-DE");            
$Einstellungen.add("oobeSystem/Microsoft-Windows-International-Core/UILanguage","de-DE");            
$Einstellungen.add("oobeSystem/Microsoft-Windows-International-Core/InputLocale","0407:00000407");            
Set-SCVMTemplate -VMTemplate $Vorlage -UnattendSettings $Einstellungen

 

“Template_Name” muss hier natürlich durch den Namen der eigenen VM-Vorlage ausgetauscht werden. Das ganze sollte dann so aussehen:

scvmmregio3

Nun ist die VM-Vorlage entsprechend angepasst, was man per PowerShell abfragen kann:

(Get-SCVMtemplate | where {$_.Name  -eq "Name_der_Vorlage"}).UnattendSettings

scvmmregio4

Schreibe einen Kommentar...

SCCM: Laufwerk C:\ auf CAS läuft wegen SCCMContenLib voll

Erst kürzlich bekam ich von unserem SCCM-System eine E-Mail mit folgendem Betreff:

Warnung: Warnung: Nicht genügend Speicherplatz für Datenbank an Standort "CAS" vorhanden

Als ich der Sache auf den Grund ging, stellte ich fest, dass der Ordner C:\SCCMContentLib ziemlich groß geworden ist:

contenlib1

Der Grund hierfür ist, dass auch auf einer CAS – wo kein DP vorhanden ist – Daten für die Verteilung abgelegt werden, wenn dieser Content auf der CAS angelegt wurde, was im Sinne einer zentralen Verteilung sicher oft gemacht wird. Da aber nun kein DP vorhanden ist, kann man das Laufwerk nicht beeinflussen, auf dem diese Daten abgelegt werden sollen. Daher wird standardmäßig das Laufwerk verwendet, welches über den meisten freien Speicherplatz verfügt (zum Zeitpunkt der Installation der CAS).

Dies hätte man verhindern können, wenn man vor der Installation der ContentLibrary auf der CAS ein leeres File mit dem Namen “no_sms_on_drive.sms” im Laufwerks-Root des Laufwerkes ablegt, auf dem keine SCCM-Daten liegen sollen.

Um nun aber nachträglich die Daten von einem Laufwerk auf ein anderes zu bekommen, ist das Tool “ContenLibraryTransfer” gedacht, welches sich im Configuration Manager Toolkit befindet.

Neben diesem Tool sind dort auch noch weitere enthalten:

contenlib2

Hinweis: Es wird immer nur die aktuellste Version von Microsoft angeboten – also aktuell 2012 R2. Diese Version funktioniert auch z.B. auf einem SCCM 2012 SP1!

Die Benutzung ist relativ einfach:

C:\Program Files (x86)\ConfigMgr 2012 Toolkit R2\ServerTools\ContentLibraryTransfer.exe –SourceDrive <AktuellesLaufwerk> –TargetDrive <ZielLaufwerk>

Das Tool kopiert dann die Inhalte auf das Ziellaufwerk und sorgt dafür, dass alle internen Verweise entsprechend geändert werden. Außerdem wird anschließend auf dem Quelllaufwerk eine NO_SMS_ON_DRIVE.SMS angelegt.

Am Ende sollte es dann so aussehen:

contenlib3

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