Microsoft Premier Field Engineer Michael Hildebrand beschreibt in seinem Blog, wie man die Tools-Leiste oben rechts im Servermanager anpassen und um weitere Einträge ergänzen kann:
Kern des Ganzen ist der Ordner „C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Administrative Tools\“
Dort kann man entsprechende Verknüpfungen ablegen:
Das funktioniert sogar mit Ordnern, um besser strukturieren zu können! Einzig die Icons, die von anderen Systemen abgelegt worden sollte man nicht verändern, weil es sonst u.U. zu Problemen kommt, etwa beim entfernen der jeweiligen Funktion.
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:
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:
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!
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.
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 😉
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($logdirin$logdirs){If(Test-Path$logdir){$foundlogfiles=Get-ChildItem$logdir-Filter smsts*.log
foreach($foundlogfilein$foundlogfiles){$logfiles+=$foundlogfile.FullName
}}}foreach($filein$logfiles){Write-Host'Parsing Logile'$file$logzeilen=Select-String-Path$file-Pattern'Failed to run the action:'foreach($zeilein$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($filein$logfiles){$logzeilen=Select-String-Path$file-Pattern'Successfully completed the action'foreach($zeilein$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($filein$logfiles){$logzeilen=Select-String-Path$file-Pattern'Execution of task sequence failed'if($logzeilen.Count -gt0){$tsfailed=$true}}if($tsfailed){Write-Host-Foregroundcolor Red 'The whole TaskSequence failed to run!'}if($notfound-gt0){Write-Host-ForegroundColor Yellow 'At least one action failed. TaskSequence possibly aborted!'Write-Host-ForegroundColor Red 'Failed actions:'foreach($pakin$failed){Write-Host$pak}}Write-Host-ForegroundColor Green 'Successfully completed actions:'foreach($pakin$success){Write-Host$pak}if($notfound-gt0){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 ..."