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

Kategorie: System Center Configuration Manager

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:

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:

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

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

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)

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

 

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

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

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

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:

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!

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:

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

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:

Schreibe einen Kommentar...

SCCM: Deinstallation von Anwendungen – die einfache Variante

Seit Configuration Manager 2012 gibt es neben den “Paketen” auch die “Anwendungen”, die bei denen man zusätzlich zur Installation auch gleich die Möglichkeit der Deinstallation integrieren kann. Will man nun beide Wege sinnvoll “automatisieren”, ergibt sich häufig, dass es eine Installations- und eine Deinstallations-Sammlung gibt. Wenn man nun einen Computer in die Installations-Sammlung aufnimmt, wird die entsprechende Software auf dem Gerät installiert. Entfernt man den Client aus der Installations-Sammlung, so wird die Anwendung aber nicht automatisch deinstalliert, man müsste nun erst den CLient in die Deinstallations-Sammlung aufnehmen. Diesen Prozess kann man etwas vereinfachen:

Die Software “Adobe Reader” wurde 2x bereitgestellt – einmal für die Installation und einmal für die Deinstallation:

Der Trick ist nun, die Deinstallations-Sammlung über Mitgliedschaftsregeln automatisch befüllen zu lassen, und zwar so, dass das gewünschte Gerät, sobald man es aus der Installations-Sammlung entfernt hat, in die Deinstallations-Sammlung wandert – und das automatisch.

Dazu hinterlegt man an der Deinstallations-Sammlung Mitgliedschaftsregeln nach dieser Form:

Nun wird die Software also auf allen Geräten entfernt, die nicht in der Installations-Sammlung enthalten sind. Dadurch wird das Handling vereinfacht:

  • Gerät in Installations-Sammlung: Installation wird durchgeführt, wenn Software noch nicht vorhanden ist
  • Gerät NICHT in Installations-Sammlung: Deinstallation wird durchgeführt, falls Software vorhanden ist
Schreibe einen Kommentar...

SCCM: PowerShell Skript, um fehlende Packages für Tasksequenzen ausfindig zu machen

In unserer produktiven SCCM-Umgebung werden insbesondere die größeren Content-Pakete per Prestaging und USB-Datenträger verteilt. Dabei kann es vorkommen, das man den Überblick, welches Paket noch auf welchem Distribution-Point fehlt bzw. für welche Tasksequenzen wo noch Pakete fehlen, verloren geht.

Um dieses Problem zu lösen, habe ich ein PowerShell-Skript geschrieben. Dazu habe ich mich von Inhalten von Jason Scheffelmaer inspirieren lassen. Um das Skript nutzen zu können, benötigt man die SCCM.psm1 von Michael Niehaus.

Das Skript wird ohne Parameter aufgerufen und fragt alle notwendigen Werte bei der Abarbeitung ab. Wichtig ist, dass die x86-Variante der PowerShell verwendet wird. Im Skript selber muss noch der Pfad zur SCCM.psm1 angepasst werden.

 

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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# Get-SCCMMissingTSPackages.ps1
 
# Author: Haiko Hertes
# Parts of the Script are taken from CheckTSPrompt.ps1 from Jason Scheffelmaer
# Needs sccm.psm1 from Michael Niehaus, tested with Version 1.5
 
# ------------- DISCLAIMER -------------------------------------------------
# This script code is provided as is with no guarantee or waranty concerning
# the usability or impact on systems and may be used, distributed, and
# modified in any way provided the parties agree and acknowledge the 
# Microsoft or Microsoft Partners have neither accountabilty or 
# responsibility for results produced by use of this script.
#
# Microsoft will not provide any support through any means.
# ------------- DISCLAIMER -------------------------------------------------
 
# The CheckPackages function checks each task sequence, finding all the packages
# that it references.  It then checks each appropriate DP to make sure that the
# needed packages are available on on those DPs.
 
function CheckPackages
{
    process
    {
        # Get the list of packages referenced by this task sequence
        $tsID = $_.PackageID
        $tsName = $_.Name
 
        # Setting up a Text-List of all DPs to see, which DP has all needed packages
        $DPsWithAllPackages = New-Object System.Collections.ArrayList
        foreach ($dp in $global:SelecteddpList)
        {
                $DPsWithAllPackages.Add($dp.Name)
        }
 
        Write-Host "###############################################################################################"
        Write-Host 'Checking the Packages for  Task Sequence $tsID ("' -NoNewLine; Write-Host $tsName -NoNewLine -ForegroundColor Yellow ; Write-Host '")'
        $tsReferences = Get-SCCMObject SMS_TaskSequenceReferencesInfo "PackageID='$tsID' and ProgramName='*'"    
 
        # Check each package
        Write-Verbose "Checking all packages..."
        foreach ($tsPackage in $tsReferences)
        {
 
            # Get a list of DPs for this package
            $tsPackageID = $tsPackage.ReferencePackageID
 
            Write-Verbose "Checking Package: $tspackageID"
            $tsReferenceDPs = Get-SCCMObject SMS_TaskSequenceReferenceDPs "TaskSequenceID='$tsID' and PackageID='$tsPackageID'"
            $packageNeededSomeWhere = $false
            $packageNeedingDPs = New-Object System.Collections.ArrayList
 
            # Check each DP to see if the package is on it
            foreach ($dp in $global:SelecteddpList)
            {
                $dpSiteCode = $dp.SiteCode
                $dpName = $dp.Name
                Write-Verbose "Checking DP $dpName"
                $found = $false
                foreach ($tsDP in $tsReferenceDPs)
                {
 
                    if ($tsDP.ServerNALPath -eq $dp.NALPath)
                    {
                        Write-Verbose "$tsPackageID - Package Found"
                        $found = $true
                        break
                    }
                }
                if ($found)
                {
                    New-Object PSObject -Property @{SiteCode=$dpSiteCode; PackageID=($tsPackageID); ServerName=($dp.ServerName); ShareName=($dp.ShareName); NALPath=($dp.NALPath); Result="Found"}
                }
                else
                {
                    # Check to see if this DP is a PXE Service Point, and if so mark as Not Needed.
                    if ($tsPackage.ReferencePackageType -eq 258 -or (-not $dp.NALPath.Contains("SMSPXEIMAGES$")))
                    {
                        Write-Verbose "$tsPackageID - Package Not Found"
                        $packageNeededSomehere = $true
                        $packageNeedingDPs.Add("$dpName |")
                        $DPsWithAllPackages.Remove($dp.Name)
                        New-Object PSObject -Property @{SiteCode=$dpSiteCode; PackageID=($tsPackageID); ServerName=($dp.ServerName); ShareName=($dp.ShareName); NALPath=($dp.NALPath); Result="Not Found"}
                    }
                    else
                    {
                        New-Object PSObject -Property @{SiteCode=$dpSiteCode; PackageID=($tsPackageID); ServerName=($dp.ServerName); ShareName=($dp.ShareName); NALPath=($dp.NALPath); Result="Not Needed"}
                    }
                }
            }
            If ($packageNeededSomehere -eq $true)
            {
                Write-Host "===&gt; " -NoNewLine; Write-Host "$tsPackageID" -ForegroundColor Red -NoNewline; Write-Host " is needed on: " -NoNewline; Write-Host $packageNeedingDPs
                $packageNeededSomehere = $false
            }
            else
            {
                Write-Host "===&gt; " -NoNewLine; Write-Host "$tsPackageID" -ForegroundColor Green -NoNewline; Write-Host " is found on ALL selected DPs!"
            }
        }
        Write-Host "Tasksequence " -NoNewline; Write-Host "$tspackageID" -ForegroundColor Yellow -NoNewline; Write-Host " could be used on " -NoNewLine; Write-Host "$DPsWithAllPackages" -ForegroundColor Yellow
        Write-Host "###############################################################################################"
    }
}
 
# ----------
# Main logic
# ----------
 
# Clear screen for the script
cls
 
# Prompt for ConfigMgr Server Name
$ConfigMgr_Server = Read-Host "Enter Hostname of SCCM-Server to connect to, [ENTER] for localhost"
If ($ConfigMgr_Server -eq ''){$ConfigMgr_Server = hostname}
Write-Verbose "$ConfigMgr_Server is selected"
 
# Connect to ConfigMgr
Import-Module "D:\SOURCES\SCCM.psm1" -force
Write-Host "Connecting to SCCM Server " -NoNewLine; Write-Host $ConfigMgr_Server -ForegroundColor Yellow
New-SCCMConnection -serverName $ConfigMgr_Server
 
# Get the list of all task sequences to choose from via a menu
Write-Host "Getting a list of all Task Sequences to select from"
Get-SCCMTaskSequencePackage | Sort-Object Name | ft Name,PackageID
 
# Prompt for Task Sequence to use when checking packages
$taskSequenceName = Read-Host 'Select Task Sequence (PackageID); [ENTER] to Cancel, "All" to check all Tasksequences'
If ($taskSequenceName -eq 'Cancel' -or $taskSequenceName -eq $null)
{
 
    Write-Host 'No Task Sequence selected or user selected "Cancel", exiting script...'
    Exit 
 
}
ElseIf ($taskSequenceName -eq 'All')
{
    $SelectedTS = $TaskSequences
}
Else
{
    $SelectedTS = $TaskSequences | ? {$_.Name -eq $taskSequenceName}
    $SelectedTSID = $SelectedTS.PackageID
 
}
$SelectedTSID = $taskSequenceName
 
# Get the list of all DPs to choose from via a menu
Write-Host "Getting a list of all Distribution Points to select from"
$global:dpList = Get-SCCMObject SMS_DistributionPointInfo
$DPNameList = ($global:dpList| Select-Object @{Name="Name"; Expression={$_.ServerName}}, NalPath, SiteCode, SiteName) | Sort-Object Name
($global:dpList| Select-Object @{Name="Name"; Expression={$_.ServerName}}, SiteCode, SiteName) | Sort-Object Name
 
# Prompt for Distribution Point to check the referenced packages against
$global:DPName = Read-Host 'Enter Name of desired Distribution Point (FQDN); [ENTER] for localhost, "All" to check all DPs'
If ($global:DPName -eq "")
{
    Write-Verbose "[ENTER] pressed, setting localhost"
    $global:DPName = ([System.Net.Dns]::GetHostByName(($env:computerName))).HostName
}
 
If ($global:DPName -eq 'All')
{
    Write-Verbose '"All" entered'
    $global:SelecteddpList =  $DPNameList
}
Else
{
    $global:SelecteddpList =  $DPNameList | ? {$_.Name -eq $global:DPName}
}
 
# Get the package status for all packages referenced 
# by the task sequence selected.
# Write-Host "Calling the CheckPackages function"
If ($taskSequenceName -eq 'All')
{
    Write-Verbose '"All" entered'
    $packageResults = Get-SCCMTaskSequencePackage | CheckPackages
}
Else
{
    Write-Verbose 'Selected DP is $SelectedTSID'
    $packageResults = Get-SCCMTaskSequencePackage -filter "PackageID='$SelectedTSID'"| CheckPackages
}

# Get-SCCMMissingTSPackages.ps1 # Author: Haiko Hertes # Parts of the Script are taken from CheckTSPrompt.ps1 from Jason Scheffelmaer # Needs sccm.psm1 from Michael Niehaus, tested with Version 1.5 # ------------- DISCLAIMER ------------------------------------------------- # This script code is provided as is with no guarantee or waranty concerning # the usability or impact on systems and may be used, distributed, and # modified in any way provided the parties agree and acknowledge the # Microsoft or Microsoft Partners have neither accountabilty or # responsibility for results produced by use of this script. # # Microsoft will not provide any support through any means. # ------------- DISCLAIMER ------------------------------------------------- # The CheckPackages function checks each task sequence, finding all the packages # that it references. It then checks each appropriate DP to make sure that the # needed packages are available on on those DPs. function CheckPackages { process { # Get the list of packages referenced by this task sequence $tsID = $_.PackageID $tsName = $_.Name # Setting up a Text-List of all DPs to see, which DP has all needed packages $DPsWithAllPackages = New-Object System.Collections.ArrayList foreach ($dp in $global:SelecteddpList) { $DPsWithAllPackages.Add($dp.Name) } Write-Host "###############################################################################################" Write-Host 'Checking the Packages for Task Sequence $tsID ("' -NoNewLine; Write-Host $tsName -NoNewLine -ForegroundColor Yellow ; Write-Host '")' $tsReferences = Get-SCCMObject SMS_TaskSequenceReferencesInfo "PackageID='$tsID' and ProgramName='*'" # Check each package Write-Verbose "Checking all packages..." foreach ($tsPackage in $tsReferences) { # Get a list of DPs for this package $tsPackageID = $tsPackage.ReferencePackageID Write-Verbose "Checking Package: $tspackageID" $tsReferenceDPs = Get-SCCMObject SMS_TaskSequenceReferenceDPs "TaskSequenceID='$tsID' and PackageID='$tsPackageID'" $packageNeededSomeWhere = $false $packageNeedingDPs = New-Object System.Collections.ArrayList # Check each DP to see if the package is on it foreach ($dp in $global:SelecteddpList) { $dpSiteCode = $dp.SiteCode $dpName = $dp.Name Write-Verbose "Checking DP $dpName" $found = $false foreach ($tsDP in $tsReferenceDPs) { if ($tsDP.ServerNALPath -eq $dp.NALPath) { Write-Verbose "$tsPackageID - Package Found" $found = $true break } } if ($found) { New-Object PSObject -Property @{SiteCode=$dpSiteCode; PackageID=($tsPackageID); ServerName=($dp.ServerName); ShareName=($dp.ShareName); NALPath=($dp.NALPath); Result="Found"} } else { # Check to see if this DP is a PXE Service Point, and if so mark as Not Needed. if ($tsPackage.ReferencePackageType -eq 258 -or (-not $dp.NALPath.Contains("SMSPXEIMAGES$"))) { Write-Verbose "$tsPackageID - Package Not Found" $packageNeededSomehere = $true $packageNeedingDPs.Add("$dpName |") $DPsWithAllPackages.Remove($dp.Name) New-Object PSObject -Property @{SiteCode=$dpSiteCode; PackageID=($tsPackageID); ServerName=($dp.ServerName); ShareName=($dp.ShareName); NALPath=($dp.NALPath); Result="Not Found"} } else { New-Object PSObject -Property @{SiteCode=$dpSiteCode; PackageID=($tsPackageID); ServerName=($dp.ServerName); ShareName=($dp.ShareName); NALPath=($dp.NALPath); Result="Not Needed"} } } } If ($packageNeededSomehere -eq $true) { Write-Host "===&gt; " -NoNewLine; Write-Host "$tsPackageID" -ForegroundColor Red -NoNewline; Write-Host " is needed on: " -NoNewline; Write-Host $packageNeedingDPs $packageNeededSomehere = $false } else { Write-Host "===&gt; " -NoNewLine; Write-Host "$tsPackageID" -ForegroundColor Green -NoNewline; Write-Host " is found on ALL selected DPs!" } } Write-Host "Tasksequence " -NoNewline; Write-Host "$tspackageID" -ForegroundColor Yellow -NoNewline; Write-Host " could be used on " -NoNewLine; Write-Host "$DPsWithAllPackages" -ForegroundColor Yellow Write-Host "###############################################################################################" } } # ---------- # Main logic # ---------- # Clear screen for the script cls # Prompt for ConfigMgr Server Name $ConfigMgr_Server = Read-Host "Enter Hostname of SCCM-Server to connect to, [ENTER] for localhost" If ($ConfigMgr_Server -eq ''){$ConfigMgr_Server = hostname} Write-Verbose "$ConfigMgr_Server is selected" # Connect to ConfigMgr Import-Module "D:\SOURCES\SCCM.psm1" -force Write-Host "Connecting to SCCM Server " -NoNewLine; Write-Host $ConfigMgr_Server -ForegroundColor Yellow New-SCCMConnection -serverName $ConfigMgr_Server # Get the list of all task sequences to choose from via a menu Write-Host "Getting a list of all Task Sequences to select from" Get-SCCMTaskSequencePackage | Sort-Object Name | ft Name,PackageID # Prompt for Task Sequence to use when checking packages $taskSequenceName = Read-Host 'Select Task Sequence (PackageID); [ENTER] to Cancel, "All" to check all Tasksequences' If ($taskSequenceName -eq 'Cancel' -or $taskSequenceName -eq $null) { Write-Host 'No Task Sequence selected or user selected "Cancel", exiting script...' Exit } ElseIf ($taskSequenceName -eq 'All') { $SelectedTS = $TaskSequences } Else { $SelectedTS = $TaskSequences | ? {$_.Name -eq $taskSequenceName} $SelectedTSID = $SelectedTS.PackageID } $SelectedTSID = $taskSequenceName # Get the list of all DPs to choose from via a menu Write-Host "Getting a list of all Distribution Points to select from" $global:dpList = Get-SCCMObject SMS_DistributionPointInfo $DPNameList = ($global:dpList| Select-Object @{Name="Name"; Expression={$_.ServerName}}, NalPath, SiteCode, SiteName) | Sort-Object Name ($global:dpList| Select-Object @{Name="Name"; Expression={$_.ServerName}}, SiteCode, SiteName) | Sort-Object Name # Prompt for Distribution Point to check the referenced packages against $global:DPName = Read-Host 'Enter Name of desired Distribution Point (FQDN); [ENTER] for localhost, "All" to check all DPs' If ($global:DPName -eq "") { Write-Verbose "[ENTER] pressed, setting localhost" $global:DPName = ([System.Net.Dns]::GetHostByName(($env:computerName))).HostName } If ($global:DPName -eq 'All') { Write-Verbose '"All" entered' $global:SelecteddpList = $DPNameList } Else { $global:SelecteddpList = $DPNameList | ? {$_.Name -eq $global:DPName} } # Get the package status for all packages referenced # by the task sequence selected. # Write-Host "Calling the CheckPackages function" If ($taskSequenceName -eq 'All') { Write-Verbose '"All" entered' $packageResults = Get-SCCMTaskSequencePackage | CheckPackages } Else { Write-Verbose 'Selected DP is $SelectedTSID' $packageResults = Get-SCCMTaskSequencePackage -filter "PackageID='$SelectedTSID'"| CheckPackages }

 

 

Download-Link zum Skript:

www.hertes.net/wp-content/uploads/2013/06/Get-SCCMMissingTSPackages.ps1

Link zur sccm.psm1:

http://blogs.technet.com/b/mniehaus/archive/2010/04/07/make-sure-a-configmgr-task-sequence-has-all-the-packages-it-needs.aspx

(Enthalten in CheckTaskSequences.zip)

Schreibe einen Kommentar...

SCCM2012: Durch SEDO dauerhaft gesperrte Tasksequenzen entsperren

SEDO (Serialized Editing of Distributed Objects) soll im SCCM2012-Umfeld möglich machen, dass mehrere Admins gleichzeitig arbeiten, ohne sich gegenseitig in die Quere zu kommen. Wenn unter SCCM2007 ein Admin eine Tasksequenz bearbeitet hat, die noch bei einem anderen Admin zur Bearbeitung geöffnet war, dann „gewann“ derjenige, der zuletzt gespeichert hat, was mitunter sehr ärgerlich war.

Unter SCCM 2012 werden nun Tasksequenzen, die zum Bearbeiten geöffnet sind, gesperrt. Diese an sich sehr nützlich Funktion kann aber auch zu einem Problem werden. Die Sperre sollte nach 30 Minuten „Inaktivität“ automatisch aufgehoben werden. Da dies aber ein Vorgang ist, den die SCCM-Konsole durchführt, kann es passieren, das eine gesperrte Tasksequenz dauerhaft gesperrt bleibt, wenn z.B. die Konsole abgestürzt ist.

Für die Sperrung ist die SQL-Tabelle SEDO_LockState zuständig.

Gesperrte Tasksequenzen kann man z.B. mit folgendem SQL-Statement abfragen:

1
SELECT * FROM SEDO_LockState WHERE LockStateID != 0

Select * FROM SEDO_LockState WHERE LockStateID != 0

Das eine Tasksequenz gesperrt ist, wird auch beim Versuch, diese zu bearbeiten angezeigt:

Um nun eine Entsperrung zu erzwingen, ist folgendes SQL-Statement geeignet:

1
2
3
4
5
6
7
8
9
UPDATE [CM_LPZ].[dbo].[SEDO_LockState]
SET [LockStateID] = 0
,[AssignedUser] = NULL
,[AssignedObjectLockContext] = NULL
,[AssignedMachine] = NULL
,[AssignmentTime] = NULL
 
WHERE [LockStateID] = '1'
GO

UPDATE [CM_LPZ].[dbo].[SEDO_LockState] SET [LockStateID] = 0 ,[AssignedUser] = NULL ,[AssignedObjectLockContext] = NULL ,[AssignedMachine] = NULL ,[AssignmentTime] = NULL WHERE [LockStateID] = '1' GO

(das „LPZ“ in „CM_LPZ“ ist durch den Sitecode zu ersetzen)

Aber Achtung: Microsoft unterstützt das manuelle Ändern der SQL-Datenbank nicht – ihr solltet also genau wissen, was ihr da tut!

2 Comments

SCCM2012: Content-Prestaging / Vorab bereitgestellte Inhalte

Oft müssen Content-Daten – z.B. in Form von WIM-Dateien für OSD – von einem Distribution Point (DP) zu einem anderen übertragen werden. Insbesondere bei größeren Dateien kann dies länger dauern, ein Versand der Daten auf dem Postweg wäre schneller.

Für diese Fälle bietet SCCM die Möglichkeit, Inhalte „vorab bereitzustellen“, sie quasi auf einem DP zu exportieren, um sie an einem anderen zu importieren. Hierbei ist zu beachten, dass einen Unterschied macht, ob man den selben Content, den man an einer Stelle exportiert hat an einer anderen importiert, oder den Content mit Hilfe des ursprünglichen (z.B. WIM-)Files neu erstellt. Beim neu erstellen erhält der Content z.B. eine andere ID!

Die notwendigen Schritte sind die folgenden:

1. Content exportieren („Datei für vorab bereitgestellten Inhalt erstellen“)

Dazu muss via Rechtsklick auf den jeweiligen Content die Option „Datei für vorab bereitgestellten Inhalt erstellen“ ausgewählt werden. Nun muss zum einen der Speicherort angegeben werden und zum zweiten der DP, von dem aus die Datei extrahiert werden soll. Am Ende hat man eine PKGX-Datei, eine gepackte Paketdatei. In dieser sind auch die Metadaten des Inhaltes enthalten. Es ist auch möglich, mehrere Content-Dateien in einem gemeinsamen PKGX-File zu exportieren.

 

2. Content an anderen DP verbringen

Hierzu muss die PKGX-Datei an den DP gebracht werden. Dies kann z.B. via DVD oder USB-Datenträger, aber auch per SMB-Filetransfer erfolgen.

3. DP für vorab bereitgestellten Inhalt aktivieren

Bevor Daten (Content) an einem DP importiert werden können, muss dieser erst für vorab bereitgestellte Inhalte aktiviert werden. Dies geschieht in den Eigenschaften des jeweiligen DPs:

 

4. Content importieren

Für den Import gibt es keine GUI, dies muss über Kommandozeile erfolgen. Dazu verwendet man extractcontent.exe aus dem BIN-Verzeichnis der SCCM-Installation. Mit dem Schalter /p wird das Paket angegeben, /s sorgt dafür, dass bereits vorhandener Content übersprungen wird.

 

Nun steht der Content am entfernten DP zur Verfügung!

2 Comments

SCCM2012: Fehler 0x80070002 während OSD / PXE

Mal wieder ein Fehler, den ich hier festhalten möchte, obwohl diesmal recht trivial.

Während einer PXE-gestützten Betriebssysteminstallation (OSD) trat nach der Partitionierung und Formatierung der Festplatte der Fehler 0x80070002 auf:

 

Ein Blick in das Log-File war leider nur mäßig hilfreich:

 

Da der nächste Schritt, der nun also nicht erfolgreich bearbeitet wurde, das Kopieren der WIM-Datei gewesen wäre, vermutete ich ein Problem beim Zugriff auf dem DP, und meine Vermutung fand ihre Bestätigung:

Es war einfach kein Network Access Account (Netzwerkzugriffskonto) angegeben!

Dies lässt sich recht einfach erreichen, ich denke die Bilder sind aussagekräftig genug:

 

UPDATE: Ich habe eben festgestellt, dass dieser Fehler auch trotz korrekt konfiguriertem NAA auftreten kann. In meinem Fall war die Fehlerursache dann auch eine ganz andere: Es fehlte einer Bedingungen für Distribution Points, nämlich die „Windows-Authentifizierung“ des IIS!

Die vollständigen Vorbedingungen lassen sich im Übrigen hier nachlesen: http://technet.microsoft.com/en-us/library/gg682077.aspx#BKMK_SupConfigSiteSystemReq 

Schreibe einen Kommentar...

SCCM2012: CcmSetup failed with error code 0x80004005 bzw. 0x800b0101

Ich hatte heute mal wieder ein kleineres Problem mit dem SCCM 2012 SP1, das sich wie folgt äußerte:

Auf einem Windows 7 Client wollte ich per Push-Installation den CM-Client installieren. Dies schlug jedoch mehrfach fehl, obwohl auf den ersten Blick alles in Ordnung war. Im Logfile unter C:\Windows\ccmsetup\Logs war zu lesen:

[LOG[Couldn't verify 'C:\windows\ccmsetup\MicrosoftPolicyPlatformSetup.msi' authenticode signature. Return code 0x800b0101]LOG]!><time="15:09:23.921-60" date="03-12-2013" component="ccmsetup" context="" type="3" thread="1600" file="util.cpp:1236">
[LOG[A Fallback Status Point has not been specified. Message with STATEID='316' will not be sent.]LOG]!><time="15:09:23.921-60" date="03-12-2013" component="ccmsetup" context="" type="1" thread="1600" file="ccmsetup.cpp:9428">
[LOG[InstallFromManifest failed 0x80004005]LOG]!><time="15:09:23.921-60" date="03-12-2013" component="ccmsetup" context="" type="3" thread="1600" file="ccmsetup.cpp:7086">
[LOG[CcmSetup failed with error code 0x80004005]LOG]!><time="15:09:23.921-60" date="03-12-2013" component="ccmsetup" context="" type="1" thread="1624" file="ccmsetup.cpp:10544">

Insbesondere dieser Eintrag machte mich etwas stutzig:

Couldn’t verify ‚C:\windows\ccmsetup\MicrosoftPolicyPlatformSetup.msi‘ authenticode signature. Return code 0x800b0101

Nach kurzer Suche war die Fehlerquelle gefunden: Ich hatte das erste RTM-Release vom SCCM2012SP1 installiert. Hier gibt es ein Problem mit Zeitstempeln digitaler Signaturen. Daher wurde am 25.01.2013 ein Re-release des SP1 veröffentlicht. Mit diesem wäre der Fehler nicht aufgetreten.

Es gibt aber auch eine andere Lösung, ohne den SCCM neu installieren zu müssen: Microsoft hat in der KB einen entsprechenden Artikel samt passendem Hotfix veröffentlicht:

http://support.microsoft.com/kb/2801987/de

Der Direktlink zum Hotfix:

http://hotfixv4.microsoft.com/ConfigMgrV5/sp1/ConfigMgr_2012_SP1_CU0_KB2801987_ENU/05.00.7804.1108/free/457899_ENU_x64_zip.exe

 

Hinweis zu den ISOs:

mu_system_center_2012_configuration_manager_and_endpoint_protection_with_sp1_x86_x64_dvd_1360357

-> Das ist das erste Release (RTM) – fehlerhaft

mu_system_center_2012_configuration_manager_and_endpoint_protection_with_sp1_x86_x64_dvd_1565907

-> Das ist das bereinigte Re-Release

Schreibe einen Kommentar...