OSD Drive Letter gotcha to look out for after upgrading to SCCM 2012 SP1
After upgrading to SCCM 2012 SP1 our OSD process was failing due to Windows coming up after mini setup still using the D: drive versus the expected C: drive.
After some digging I found the culprite in the Task Sequence. There is a Task called “Set Variable for Drive Letter” which sets the OSD Variable “OSDPreserveDriveLetter=False”
This is new functionality in SP1 that allows you to override the drive letter (using variable OSDisk) versus the old method that would always use the drive letter from the WIM file (the drive it was captured on).
After disabling this step, all was right with the world again as I do want to always use the C: drive from my WIM image.
Hope this helps someone else!
More info about this variable can be found here:
http://technet.microsoft.com/en-us/library/hh273375
Preparing for SCCM 2012 Client Upgrade
Thought I would throw this gotcha out in case anyone else runs into this.
The scenario: Installing SCCM 2012 Client Prerequisites prior to your upgrade.
After updating my WIM image with all the prereqs (that are applicable) from the SCCM 2012 Client directory, I then did a test deployment against my existing SCCM 2007 Infrastructure. My Task Sequence step that installs FEP 2010 failed. (Fep-ApplyPolicy-WCOMPUTERNAME.log shows “ERROR FEP Client Installation failed. Error Code 0x8004FF52″
Upon further investigation it was the SCCM 2012 prerq “Windows Firewall Configuration Provider” causing the fail.
Specifically the following message is displayed if you run the install with the GUI:
“One or more programs on your computer conflict with Forefront Endpoint Protection. To Install Forefront Endpoint Protection, you must remove the following programs and then run the installation wizard again:
Windows Firewall Configuration Provider”
So long story short, do not pre-deploy this update prior to your SCCM 2012 client upgrade.
So far the other prereqs seem to be fine and highly recommend you deploy these prior to your client upgrade otherwise the client install can be VERY lengthy. Especially .NET Framework 4.0.
*A side note, in my environment I also added Silverlight 5 to my image and will be deploying that out to all existing clients with the other prereqs as well (excluding Windows Firewall Configuration Provider of course!). Silverlight 4 is included with SCCM 2012 RTM Client however SCCM 2012 SP1 will require Silverlight 5 and its backwards compatible with SCCM RTM.
One of the most underated changes in SCCM 2012 OS Deployment
In my opinion of course!
One of the most glaring oversights in OS Deployment in SCCM 2007 was the fact that doing a “Push” deployment of an Operating System where you wanted to downloads all code prior to executing the Task Sequence, resulted in the code not starting to download until the start of the Task Sequence advertisement.
Example:
You create an OS deployment for machine X and you want that machine to rebuild overnight. At let’s say at 2:00PM you create this deployment with an availability time of “immediate” but a forced execution time of for 10:00PM. Well unfortunately, the code doesn’t START downloading until 10:00PM when the mandatory advert kicks off.
If you are super connected with high bandwidth to all your clients (we all have that luxury don’t we??), this is a very moot point. However, my guess is that if you had that sort of bandwidth, you likely set it to “download as needed during the Task Sequence”. In my environment I unfortunately have many low bandwidth site that I would like to “prestage” the 8+ GB of data required to rebuild a machine by having it download throughout the day and be available and ready to go once 10:00 PM rolls around.
This boggled my mind why all standard package distributions will download immediately and lie in wait for the mandatory execution time, but the code for OSD waits until time of execution to start downloading. This (again in my opinion) seems like it would be the MOST likely candidate for download ahead of time given the massive amount of data that goes along with a typical OS Deployment process.
Never being satisfied, I tried a few scenarios basically trying to prestage the code and have OSD see it was already there and just start using but alas my efforts were thwarted. The end result for me was Push based deployment were all but abandoned as the sites I really needed it at were low bandwidth thus the code would take all night and most of the time into the morning to complete. As you may have guessed this was frustrating to the office user who strolls in, grabs some coffee and sits down to begin their day. Just about the time they get all their apps up and running, the message would finally pop letting them know their machine was about to be rebuilt. :-S
Fast forward to SCCM 2012. Task Sequence deployment now DOWNLOADS AT AVAILABILITY TIME!! Let me repeat that… Task Sequence deployment now DOWNLOADS AT AVAILABILITY TIME!! Yes, this means you can schedule the deployment well ahead of time and the code can be ready and waiting in the SCCM Client cache for the mandatory execution time.
Maybe I am the only person who continued to hound the folks at Microsoft about the lack of this functionality in CM 2007 (highly doubt it) as I have yet to hear anyone talk about this change in functionality. This is HUGE NEWS!! I even asked specifically about this at MMS 2012 and no one pointed out that this is now native functionality?!
If you have run up against this limitation in SCCM 2007, sleep well knowing that 2012 has finally brought BACK the OS Push deployments! Now you just need to get your lab up and running and start testing away!
SCCM Query for Empty Collections
I just came across the need to query SCCM for collections that are empty as digging through queries in the console I could not find a suitable solution. Luck would have it that a quick Google search away was the following:
Here’s the actual query statement
select
C.Collectionid,
C.Name
from
dbo.v_Collection C
Where
C.Collectionid not in (Select Collectionid from dbo.v_FullCollectionMembership)
Pretty basic however if your even remotely familiar with SQL you can add additional critera to weed out collections you know will be empty and only look at the ones you truly care about.
Example: In our SCCM we have multiple layers of collections with the parent collections being empty (with no “update this collection” schedule of course!)
Lets pick on Adobe. For Acrobat Reader we have a parent collection called Adobe, a sub collection called Adobe_AcrobatReader and the finally a 3rd level collection called Adobe_AcrobatReader_9.3_R01-Install.
The query above returns all 3 collections when really all I care about is the 3rd level collection. The other two collections are empty by design.
Quite simply I add another criteria to my query to only pull back the “Install” collections like so:
select
C.Collectionid,
C.Name
from
dbo.v_Collection C
Where
C.Collectionid not in (Select Collectionid from dbo.v_FullCollectionMembership) and c.name like '%-Install'
Easy peasy lemon squeezy.
SCCM: Migrating to a new source code server
I have run across this several time in the past where the source code location for your packages (and now drivers as well) needs to be moved to a new server. I have 3 scripts I use (Could be combined into one if you like) that will allow you to change the location for each object.
Script 1: remapPkgs.vbs
This script will modify the source path for all (non Driver related) packages.
Script 2: remapDrivers.vbs
This script will remap all the source locations for the drivers that have been imported.
Script 3: remapDriverPkgs.vbs
This script will remap all the source locations for your driver packages.
VERY IMPORTANT – When you run the remapPkgs.vbs and the remapDriverPkgs.vbs it will trigger a Distribution Point update for all your packages. You will likely want to run this after hours or over the weekend depending on how many packages you have and what your bandwidth is to your DP’s.
*UPDATE* – Couple of things I noticed in my environment after running these scripts.
1. We use Nomad Branch from 1E. This adds a tab to your packages where you specify if you want to enable Nomad. After running the scripts I had to go back and reset this on all my packages.
2. Much more importantly we also discovered that these scripts re-enabled the setting for all of our App-V packages to “remove when no longer advertised”. Under normal day to day operations this setting is desired for us however as part of our re-assigning the clients to a new site code all local policy is deleted and re-downloaded from the new server. Well with that option still enabled it would remove all App-V apps from all machines as the policy would get deleted and it would not come back automatically. This is why we removed that option. Long story short we had to go back and remove the option again from all App-V packages after migrating the source code.
Another important note regarding App-V packages:
Although Microsoft does not give you the option to modify the source directory of your Virtual Applications via the Admin Console the script above will remap your App-V packages as well. In my environment however I was required to touch each Virtual App after the script and “re-import” the package if you will. This was achieved by clicking the Properties of the Virtual App, Click Data Source, then click “Set”. In the “Virtual Application package source directory” click “Browse” and specify the original location of the xml file used to create the package. I found that in most circumstances I needed to delete the contents of the “ConfigMgr data source directory” prior to browsing to the xml file. Once you import the package again the contents of the source directory get re-created and the package resent to the DP’s.
The syntax for the scripts are included in the script header however its pretty simple (must be run from the SCCM server itself):
cscript scriptname.vbs NEWSERVERNAME
So example: My current source path that I want to change is:
\\SERVER01\SOURCE$\SoftwareInstalls\Adobe_AcrobatReader_9.0_R01
I would run the script like so:
cscript remapPkgs.vbs SERVER02
This will change the source directory to:
\\SERVER02\SOURCE$\SoftwareInstalls\Adobe_AcrobatReader_9.0_R01
(And trigger an update to the DP)
Again these scripts are setup to do all packages in one fell swoop and designed to only need to replace the server name. You could easily modify the scripts to change the share name, etc as needed.
I would also recommend commenting out the “put_” statement and run the script to verify it works prior to actually making the changes.
_________________________________________________
Script 1 (Mind word wrapping)
'///////////////////////////////////////////////////
'
' SYNTAX: cscript remapPkgs.vbs NEWSERVERNAME
'
'///////////////////////////////////////////////////////////
Dim oWbemServices
If UCase(Right(Wscript.FullName, 11)) = "WSCRIPT.EXE" Then
Wscript.Echo "This script must be run under CScript."
Wscript.Quit
End If
If WScript.Arguments.Count 1 Then
WScript.Echo "Wrong number of arguments were passed."
WScript.Quit
End If
'====================================================
'== Execute SUB to connect to SCCM Provider
'====================================================
CONNSCCM()
For Each oPackage In oWbemServices.execquery("select * from sms_package")
sOldPathString = oPackage.pkgsourcepath
If InStr(sOldPathString,"\\") Then
iStart = InStr(3,sOldPathString,"\")
sNewPathString = Right(sOldPathString,(LEN(sOldPathString)-iStart))
sNewPathString = "\\" & WScript.Arguments(0) & "\" & sNewPathString
WScript.Echo "Setting Package Path for " & oPackage.Name & " from " & sOldPathString & " to " & sNewPathString
oPackage.pkgsourcepath = sNewPathString
oPackage.put_
End If
Next
WScript.Echo "Done"
'====================================================
' SUB: Connects to the SMS Provider
'====================================================
Sub CONNSCCM()
Set oWbemLocator = CreateObject("WbemScripting.SWbemLocator")
Set oWbemServices = oWbemLocator.ConnectServer(".", "root\sms")
Set oSCCMProvLoc = oWbemServices.InstancesOf("SMS_ProviderLocation")
For Each oLoc In oSCCMProvLoc
If oLoc.ProviderForLocalSite = True Then
Set oWbemServices = oWbemLocator.ConnectServer(oLoc.Machine, "root\sms\site_" + oLoc.SiteCode)
End If
Next
End Sub
Script 2 (Mind word wrapping)
'///////////////////////////////////////////////////
'
' SYNTAX: cscript remapDrivers.vbs NEWSERVERNAME
'
'///////////////////////////////////////////////////////////
Dim oWbemServices
If UCase(Right(Wscript.FullName, 11)) = "WSCRIPT.EXE" Then
Wscript.Echo "This script must be run under CScript."
Wscript.Quit
End If
If WScript.Arguments.Count 1 Then
WScript.Echo "Wrong number of arguments were passed."
WScript.Quit
End If
'====================================================
'== Execute SUB to connect to SCCM Provider
'====================================================
CONNSCCM()
For Each oPackage In oWbemServices.execquery("select * from sms_Driver")
sOldPathString = oPackage.contentsourcepath
If InStr(sOldPathString,"Latitude 13") Then
iStart = InStr(3,sOldPathString,"\")
sNewPathString = Right(sOldPathString,(LEN(sOldPathString)-iStart))
sNewPathString = "\\" & WScript.Arguments(0) & "\" & sNewPathString
WScript.Echo "Setting Package Path for " & oPackage.LocalizedDisplayName & " from " & sOldPathString & " to " & sNewPathString
'WScript.Echo sOldPathString
oPackage.contentsourcepath = sNewPathString
oPackage.put_
End If
Next
WScript.Echo "Done"
'====================================================
' SUB: Connects to the SMS Provider
'====================================================
Sub CONNSCCM()
Set oWbemLocator = CreateObject("WbemScripting.SWbemLocator")
Set oWbemServices = oWbemLocator.ConnectServer(".", "root\sms")
Set oSCCMProvLoc = oWbemServices.InstancesOf("SMS_ProviderLocation")
For Each oLoc In oSCCMProvLoc
If oLoc.ProviderForLocalSite = True Then
Set oWbemServices = oWbemLocator.ConnectServer(oLoc.Machine, "root\sms\site_" + oLoc.SiteCode)
End If
Next
End Sub
Script 3 (Mind word wrapping)
'///////////////////////////////////////////////////
'
' SYNTAX: cscript remapDriverPkgs.vbs NEWSERVERNAME
'
'///////////////////////////////////////////////////////////
Dim oWbemServices
If UCase(Right(Wscript.FullName, 11)) = "WSCRIPT.EXE" Then
Wscript.Echo "This script must be run under CScript."
Wscript.Quit
End If
If WScript.Arguments.Count 1 Then
WScript.Echo "Wrong number of arguments were passed."
WScript.Quit
End If
'====================================================
'== Execute SUB to connect to SCCM Provider
'====================================================
CONNSCCM()
For Each oPackage In oWbemServices.execquery("select * from sms_DriverPackage")
sOldPathString = oPackage.PkgSourcePath
If InStr(sOldPathString,"\\") Then
iStart = InStr(3,sOldPathString,"\")
sNewPathString = Right(sOldPathString,(LEN(sOldPathString)-iStart))
sNewPathString = "\\" & WScript.Arguments(0) & "\" & sNewPathString
WScript.Echo "Setting Package Path for " & oPackage.Name & " from " & sOldPathString & " to " & sNewPathString
'WScript.Echo sOldPathString
oPackage.PkgSourcePath = sNewPathString
oPackage.put_
End If
Next
WScript.Echo "Done"
'====================================================
' SUB: Connects to the SMS Provider
'====================================================
Sub CONNSCCM()
Set oWbemLocator = CreateObject("WbemScripting.SWbemLocator")
Set oWbemServices = oWbemLocator.ConnectServer(".", "root\sms")
Set oSCCMProvLoc = oWbemServices.InstancesOf("SMS_ProviderLocation")
For Each oLoc In oSCCMProvLoc
If oLoc.ProviderForLocalSite = True Then
Set oWbemServices = oWbemLocator.ConnectServer(oLoc.Machine, "root\sms\site_" + oLoc.SiteCode)
End If
Next
End Sub
VBScript: Run Optional SCCM Advertisements
Here’s a fairly simple script that will check for and execute all optional advertisments on a client machine. This could be modified to include checks for adverts that have already ran and not rerun or put a GUI around it to choose which adverts to run selectively.
If the code has not been downloaded to the client yet a content retrieval will be made automatically prior to running.
CAREFUL OF WORD WRAPPING
'==========================================================================
'
' VBScript Source File -- Created with SAPIEN Technologies PrimalScript 2009
'
' NAME: RunOptionalAdvertisements.vbs
'
' AUTHOR: William Bracken
' DATE : 10/12/2009
'
' COMMENT: Run optional advertisments
'
'==========================================================================
Dim oUIResource, programArray(), fs, f
Dim oPrograms, x, i, strLogFile, strFileName, UserTempPath, WindowsTempPath
Dim oProgram, programId, packageId, myProgram, WinTempLog, UsrTempLog
Const ForReading = 1, TemporaryFolder = 2, ForAppending = 8
Set fs = CreateObject("Scripting.FileSystemObject")
Set WSHShell = CreateObject("WScript.Shell")
windir = WSHShell.ExpandEnvironmentStrings("%WINDIR%")
UserTempPath = WSHShell.ExpandEnvironmentStrings("%TEMP%")
WindowsTempPath = windir & "\temp"
strFileName = "RunOptionalAdverts.log"
WinTempLog = WindowsTempPath & "\" & strFileName
UsrTempLog = UserTempPath & "\"& strFileName
strLogFile = UsrTempLog
CLEANLOG strLogFile
COLLECTMSG "Begin Logging", ""
' Validate connection to SCCM Client
Set oUIResource = CreateObject ("UIResource.UIResourceMgr")
If oUIResource Is Nothing Then
COLLECTMSG "CONNECT TO SCCM CLIENT", "Could not create Resource Object - quitting"
wscript.echo "Could not create Resource Object - quitting"
WScript.Quit
End If
'==========================================================================
' Begin Process
'==========================================================================
' Call ShowPrograms routine to get array of programs available
ShowPrograms
' Split Array And Call Sub to install
For Each program In programArray
strStrings = Split (program,";")
programId = strStrings(0)
packageId = strStrings(1)
' Call RunProgam routine to invoke installation
'RunProgram programId,packageId
Next
' Cleanup
Set oProgram=Nothing
Set oUIResource=Nothing
'==========================================================================
' Routines
'==========================================================================
Sub ShowPrograms
On Error Resume Next
Set oPrograms = oUIResource.GetAvailableApplications
If oPrograms Is Nothing Then
COLLECTMSG "SHOWPROGRAMS", "Failed to get programs object - quitting"
wscript.echo "Failed to get programs object - quitting"
Set oUIResource=Nothing
Exit Sub
End If
strCount = oPrograms.Count
COLLECTMSG "SHOWPROGRAMS", "There are " & strCount & " programs"
wscript.echo "There are " & strCount & " programs"
wscript.echo
strCount = strCount - 1
ReDim programArray(strCount)
x = 0
For Each oProgram In oPrograms
COLLECTMSG "SHOWPROGRAMS", "ProgramName:" & oProgram.Name & ";" & "PackageID:" & oProgram.PackageId
WScript.Echo "Program Name: " & oProgram.Name
WScript.Echo " Package ID: " & oProgram.PackageId
' build array
programArray(x) = oProgram.Name & ";" & oProgram.packageId
x = x + 1
Next
End Sub
Sub RunProgram(programId,packageId)
On Error Resume Next
' #region Disabled code
' if oUIResourceMgr.IsMandatoryProgramPending = 1 Then
' Wscript.Echo "Mandatory program pending. Try again later."
' Set oUIResource=Nothing
' Exit Sub
' End If
' #endregion
' Get Program object
Set oProgram = oUIResource.GetProgram(programId,packageId)
'WScript.Echo "oProgram: " & oProgram.Name
if oProgram is Nothing Then
COLLECTMSG "RUNPROGRAMS", "Couldn't get the program"
WScript.Echo "Couldn't get the program"
Set oUIResource=Nothing
Exit Sub
End If
'When was the program last ran?
Set myProgram = oUIResource.GetProgram(programId, packageId)
oldTime = myProgram.LastRunTime
COLLECTMSG "RUNPROGRAMS", "Last Run Time: " & oldTime
WScript.Echo oldTime
' Execute program
COLLECTMSG "RUNPROGRAMS", "Running program: " & programId & " " & packageId
Wscript.Echo "Running program: " & programId & " " & packageId
oUIResource.ExecuteProgram programId, packageId, True
' Wait for last ran to change...
Set myProgram = oUIResource.GetProgram(programId, packageId)
Do While myProgram.LastRunTime = oldTime
Wscript.Sleep(2500)
Set myProgram = oUIResource.GetProgram(programId, packageId)
Loop
COLLECTMSG "RUNPROGRAMS", "Return Code: " & Err
WScript.Echo "Return Code: " & Err
End Sub
'*************************************************************
'*** Method: COLLECTMSG
'*** Description: Collections Errors for logging to log file
'*************************************************************
Sub COLLECTMSG(szLocation, errmsg)
'on error resume next
'Dim f
strmsg=errmsg
Set f = fs.OpenTextFile(strLogFile, ForAppending, True)
strmsg = Now & " | " & szLocation & " | "& errmsg
if err0 then
strmsg=strmsg & vbCrLf _
& "Error | " & Hex(Err.Number) & " h (" & CStr(Err.Number) & ") | Description |" & Err.Description
end if
f.Writeline strmsg
f.Close
strmsg=""
Err.Clear
End Sub
Sub CLEANLOG(strLogFile)
If fs.FileExists(strLogFile) Then
fs.DeleteFile strLogFile
End If
End Sub
Adding Duplicate Drivers to SCCM SP1
Another one of the most glaring deficiencies in my opinion with OS deployment using SCCM is the inability to import the same drivers for different models. I personally prefer to import ALL drivers for every model of machine I have versus trying to dig through other imported drivers and piece together what each model actually needs. It can be quite frustrating to say the least. I break up my drivers by model and would love to out of the box (I believe this may be addressed in SP2) simply import the entire driver set in each subfolder, then create a package per model and assign the drivers from the subfolders to their respective package. Well the good news is you CAN! You simply have to trick SCCM into allowing you to do this. Its quite simple actually. SCCM bases whether drivers are the “same” based on the calculated hash value of the driver files. By simply adding a unique text file to the driver folder it creates a different hash value and allows you to import them even if the actual drivers have already been imported. How I do it:
Break your drivers down by model in their own subfolders:
Example:
\\Server\share$\Drivers\Latitude E6500\Audio
\\Server\share$\Drivers\Latitude E6500\Video
Etc..
\\Server\share$\Drivers\Latitude E4300\Audio
\\Server\share$\Drivers\Latitude E4300\Chipset
etc..
Now open the root folder and create a new txt file based on the model.
Example:
\\Server\share$\Drivers\Latitude E4300\Latitude E4300.txt
Now copy this file to your clipboard, then do a windows search within the root directory for “*”. Sort by type to gather all the folders at the top of the search. Right click on EACH folder within the search and past the model specific txt file.
Do this for each of your driver sets and when you import them into their own sub-folder under the drivers node they will import even if the actual driver has already been imported.
you can now make corresponding packages for each model all with their OWN version of their drivers.
I know this has made my life SO much better with managing drivers in SCCM. I hope it helps someone else.
*UPDATE* – A MUCH quicker and simpler way to create the txt file in your driver folders can be found here:
http://myitforum.com/cs2/blogs/jsandys/archive/2010/04/05/duplicate-drivers-helper-script.aspx
