Haikos Blog Blog von Haiko Hertes zu allen Themen rund um Microsoft und Datacenter

12Jun/130

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 "===> " -NoNewLine; Write-Host "$tsPackageID" -ForegroundColor Red -NoNewline; Write-Host " is needed on: " -NoNewline; Write-Host $packageNeedingDPs
                $packageNeededSomehere = $false
            }
            else
            {
                Write-Host "===> " -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
}

 

sccm-ps1 sccm-ps2

 

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)

31Mai/132

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

TS_Screen2

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

TS_Error

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

(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!

TS_Lösung