Reporting about all the different OS Deployment Versions with ConfigMgr 2012

One of the biggest challenges with OS Deployment is keeping track of all the different deployments and everything that changed between the deployments. This post will focus on the first part, keeping track of all the different deployments. I will do that by showing a way to write information to the registry, creating a hardware inventory for those registry keys and building a report on the inventory data. For the second part, keeping track of all the changes, take a look at this great post of Maik Koster.

Create registry keys

Reg_DepVerThe first thing is to create a script that will write information about the deployment to the registry. As a deployment is much more then just the image, we need information about the task sequence. There are multiple ways to put a version number somewhere in the task sequence. I choose to write a version number in the task sequence name. Besides that I also write some extra information to the registry about the different Id’s and how it was deployed (see picture). To do this I use a very basic script like this (change PETERTEST to something different, like a company name):

Dim strAdvertisementID, strOrganisationName, strTaskSequenceID, strPackageName, strMediaType, strInstallDate, strKeyPath
Dim objEnv, objReg

Set objEnv = CreateObject(“Microsoft.SMS.TSEnvironment”)
Set objReg = GetObject(“winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv”)

Const HKEY_LOCAL_MACHINE = &H80000002

‘Set variables
strAdvertisementID = objEnv(“_SMSTSAdvertID”)
strOrganisationName = objEnv(“_SMSTSOrgName”)
strTaskSequenceID = objEnv(“_SMSTSPackageID”)
strPackageName = objEnv(“_SMSTSPackageName”)
strMediaType = objEnv(“_SMSTSMediaType”)

‘Tattoo information in registry
strKeyPath = “SOFTWARE\PETERTEST”
objReg.CreateKey HKEY_LOCAL_MACHINE,strKeyPath

strValueName = “AdvertisementID”
objReg.SetStringValue HKEY_LOCAL_MACHINE,strKeyPath,strValueName,strAdvertisementID

strValueName =”InstallDate”
strInstallDate = FormatDateTime(date,2) & ” ” & FormatDateTime(time,3)
objReg.SetStringValue HKEY_LOCAL_MACHINE,strKeyPath,strValueName,strInstallDate

strValueName = “MediaType”
objReg.SetStringValue HKEY_LOCAL_MACHINE,strKeyPath,strValueName,strMediaType

strValueName = “OrganisationName”
objReg.SetStringValue HKEY_LOCAL_MACHINE,strKeyPath,strValueName,strOrganisationName

strValueName = “PackageName”
objReg.SetStringValue HKEY_LOCAL_MACHINE,strKeyPath,strValueName,strPackageName

strValueName = “TaskSequenceID”
objReg.SetStringValue HKEY_LOCAL_MACHINE,strKeyPath,strValueName,strTaskSequenceID

‘Clean up
Set objEnv = Nothing
Set objReg = Nothing

TSEd_DepVerNow to run this script, save it as a vbs file and add it to a package. In the  task sequence (see picture) add a Run Command Line –step, select Package and Browse to the package with the new script. In the Command line add cscript.exe <ScriptName> and select Disable 64-bit file system redirection. This last action makes sure that the registry information will be written to the same key on all systems, so there is no need for multiple inventory classes.

Extend Hardware Inventory

RegKeyToMofThe second thing is to create a hardware inventory for the newly created registry keys. There are two ways to do this. The first one is to create our own extensions and the second one is to use the tool RegKeyToMof. I choose to use the second way, as that tool makes my life a whole lot easier. On a computer, which has run the task sequence, open the tool RegKeyToMOFv31 and browse in the top screen to the new registry key. In the bottom screen it will now show the necessary extensions for the different MOF files. Also fill in a good Class Name (I use DeploymentVersion), as this will also be part of the table and view names in the database.

Now go to <InstallDir>\inboxes\clifiles.src\hinv, open the configuration.mof, go all the way to the bottom of this file and add the content of the configuration.mof –tab between the lines Added extensions start and Added extensions end. Assuming this is the first extensions, it will make it look similar to this (remember that PETERTEST might be different, when it’s changed in the initial script):

//========================
// Added extensions start
//========================

#pragma namespace (“\\\\.\\root\\cimv2”)
#pragma deleteclass(“DeploymentVersion”, NOFAIL)
[DYNPROPS]
Class DeploymentVersion
{
[key] string KeyName;
String AdvertisementID;
String InstallDate;
String MediaType;
String OrganisationName;
String PackageName;
String TaskSequenceID;
};

[DYNPROPS]
Instance of DeploymentVersion
{
KeyName=”Deployment_Version”;
[PropertyContext(“Local|HKEY_LOCAL_MACHINE\\SOFTWARE\\PETERTEST|AdvertisementID”),Dynamic,Provider(“RegPropProv”)] AdvertisementID;
[PropertyContext(“Local|HKEY_LOCAL_MACHINE\\SOFTWARE\\PETERTEST|InstallDate”),Dynamic,Provider(“RegPropProv”)] InstallDate;
[PropertyContext(“Local|HKEY_LOCAL_MACHINE\\SOFTWARE\\PETERTEST|MediaType”),Dynamic,Provider(“RegPropProv”)] MediaType;
[PropertyContext(“Local|HKEY_LOCAL_MACHINE\\SOFTWARE\\PETERTEST|OrganisationName”),Dynamic,Provider(“RegPropProv”)] OrganisationName;
[PropertyContext(“Local|HKEY_LOCAL_MACHINE\\SOFTWARE\\PETERTEST|PackageName”),Dynamic,Provider(“RegPropProv”)] PackageName;
[PropertyContext(“Local|HKEY_LOCAL_MACHINE\\SOFTWARE\\PETERTEST|TaskSequenceID”),Dynamic,Provider(“RegPropProv”)] TaskSequenceID;
};

//========================
// Added extensions end
//========================

After this go to the to import in Admin/AgentSettings/HardwareInventory/SetClasses/Import –tab and add the content to a new (temporary) MOF file. This will create a MOF file with the following content:

#pragma namespace (“\\\\.\\root\\cimv2\\SMS”)
#pragma deleteclass(“DeploymentVersion”, NOFAIL)
[SMS_Report(TRUE),SMS_Group_Name(“DeploymentVersion”),SMS_Class_ID(“DeploymentVersion”)]
Class DeploymentVersion: SMS_Class_Template
{
[SMS_Report(TRUE),key] string KeyName;
[SMS_Report(TRUE)] String AdvertisementID;
[SMS_Report(TRUE)] String InstallDate;
[SMS_Report(TRUE)] String MediaType;
[SMS_Report(TRUE)] String OrganisationName;
[SMS_Report(TRUE)] String PackageName;
[SMS_Report(TRUE)] String TaskSequenceID;
};

HarInvClaNow open the Configuration Manager Console and navigate to Administration > Client Settings. Open (for example) the Default Client Settings, go to Hardware Inventory and click Set Classes. In the Hardware Inventory Classes, click Import and Open the new (temporary) MOF file. This will show the new class for the inventory.

Create report

The third, and last, thing to do is to create a report based on the new registry information. This is probably a lot easier then you might think, because adding a new class to the hardware inventory also creates new tables and views in the database. As I used DeploymentVersion, as the class name for the extension of the hardware inventory, the view in the database with the information is named v_GS_DeploymentVersion. So to get the new information in a report, including computer name, we need the following query:

SELECT     v_R_System.Name0, v_GS_DeploymentVersion0.AdvertisementID0, v_GS_DeploymentVersion0.InstallDate0, v_GS_DeploymentVersion0.MediaType0,
                      v_GS_DeploymentVersion0.OrganisationName0, v_GS_DeploymentVersion0.PackageName0, v_GS_DeploymentVersion0.TaskSequenceID0
FROM         v_GS_DeploymentVersion0 INNER JOIN
                      v_R_System ON v_GS_DeploymentVersion0.ResourceID = v_R_System.ResourceID
ORDER BY v_R_System.Name0

Now to put all the pieces together, open SQL Server Report Builder 3.0 and create a new report. Add a Data Source to the ConfigMgr database and create a DataSet with the previously mentioned query. The last things to do is add a table to report, add the necessary fields and save the report between the other ConfigMgr Reports. Open the Configuration Manager Console, navigate to Monitoring > Overview > Reporting > Reports and that’s where the new report will show up.

Result

Wondering what the end result looks like? Well, here is a basic example of what it will look like:

Rep_DepVer

ConfigMgr 2007 and clearing a Computers’ Last PXE Advertisement by script

In a previous post I showed a script to remove a computer from a collection. This post will be an add-on to that previous post. As we are removing the computer from the collection anyway, we can as well perform a Clear Last PXE Advertisement –action. By doing this, it’s not necessary to perform a manual action the next time the computer needs to be re-imaged.

ClrLstPXEAdvAn easy way to do this is to run a script at the end of a Task Sequence that will clear the last PXE Advertisement. This makes sure that a computer can get re-imaged as soon it gets added to the correct collection. For this you can use the script from this post.

The usage of this script is cscript <ScriptName>.vbs /ComputerName:[ComputerName]. Keep in mind that it needs to be run with an account that has enough rights in ConfigMgr. See also this picture for an example.

Option Explicit

DIM objSWbemLocator, objSWbemServices, ProviderLocation, Location, Connection
DIM colResourceID, objResourceID, iResourceID, aResources, InParams
DIM sComputerName, sSiteServerName, objArguments

sSiteServerName = “<SiteServerName>”

‘====================================
‘ Check arguments
‘====================================
Set objArguments = Wscript.Arguments
If WScript.Arguments.Count = 1 Then
   sComputerName = objArguments.Named.Item(“ComputerName”)
Else
   Wscript.Echo “Usage: ClearPxeAdvertisement.vbs /ComputerName:[ComputerName]”
   Wscript.Quit
End If

‘====================================
‘ MAIN Script
‘====================================
Set Connection = ConnectToSMSProvider(sSiteServerName)
iResourceID = GetResourceID(Connection, sComputerName)

aResources = Array(1)
aResources(0) = iResourceID

Set InParams = Connection.Get(“SMS_Collection”).Methods_(“ClearLastNBSAdvForMachines”).InParameters.SpawnInstance_
InParams.ResourceIDs = aResources

Connection.ExecMethod “SMS_Collection”,”ClearLastNBSAdvForMachines”, InParams
WScript.Echo “Cleared PXE advertisement for resource: ” & iResourceID

‘====================================
‘ Function to RETURN a Connection to the SMS Provider
‘====================================
Function ConnectToSMSProvider(ServerName)
   Set objSWbemLocator = CreateObject(“WbemScripting.SWbemLocator”)
   Set objSWbemServices = objSWbemLocator.ConnectServer(ServerName, “root\sms”)
   Set ProviderLocation = objSWbemServices.InstancesOf(“SMS_ProviderLocation”)
   For Each Location In ProviderLocation
      If Location.ProviderForLocalSite = True Then
         Set objSWbemServices = objSWbemLocator.ConnectServer(Location.Machine, “root\sms\site_” + Location.SiteCode)
         Set ConnectToSMSProvider = objSWbemServices
      End If
   Next
End Function

‘====================================
‘ Function to RETURN a ResourceID by a ComputerName
‘====================================
Function GetResourceID(Connection, ComputerName)
   Set colResourceID = Connection.ExecQuery(“Select ResourceID from SMS_R_System where Name = ‘” & ComputerName & “‘”)
   For Each objResourceID in colResourceID
      GetResourceID = objResourceID.ResourceID
   Next
End Function

WScript.Quit(0)

There also exists an (basic) example on MSDN about How to Clear a PXE Advertisement for a Configuration Manager Resource, here: http://msdn.microsoft.com/en-us/library/cc143002.aspx

ConfigMgr 2007 and removing a Computer from a Collection by script

I have to admit that it’s just really easy/ handy to create scripts to make life a bit easier. This also counts for this scenario… A customer wants to prevent, at all costs, that a computer can’t get re-imaged “by accident”. It already happened a few times that somebody by accident did a Clear Last PXE Advertisement on a Computer, or even on a Collection.

An easy solution for this scenario is to run a script at the end of a Task Sequence that will remove the Computer directly from the Collection. This makes sure that a computer can’t get re-imaged, as it’s not a member of the collection anymore. For this you can use the script from this post.

RemCompfrCollThe usage of this script is cscript <ScriptName>.vbs /CollectionID:[CollectionID] /ComputerName:[ComputerName]. Keep in mind that it needs to be run with an account that has enough rights in ConfigMgr. See also this picture for an example.

Option Explicit

DIM objSWbemLocator, objSWbemServices, ProviderLocation, Location, sSiteServerName
DIM sComputerName, sCollectionID, objCollection, colRuleSet, Rule, objArguments

‘=============================
‘ Check arguments
‘=============================
Set objArguments = Wscript.Arguments
If WScript.Arguments.Count = 2 Then
   sCollectionID = objArguments.Named.Item(“CollectionID”)
   sComputername = objArguments.Named.Item(“ComputerName”)
Else
   Wscript.Echo “Usage: RemoveComputerFromCollection.vbs /CollectionID:[CollectionID] /ComputerName:[ComputerName]”
   Wscript.Quit(0)
End If

‘=============================
‘ MAIN Script
‘=============================
sSiteServerName=”<SiteServerName>”
ConnectToSMSProvider(sSiteServerName)

Set objCollection = objSWbemServices.Get(“SMS_Collection='” & sCollectionID & “‘”) 
colRuleSet = objCollection.CollectionRules
For Each Rule In colRuleSet
    If Rule.Path_.Class = “SMS_CollectionRuleDirect” Then 
        If LCase(Trim(Rule.RuleName)) = LCase(Trim(sComputerName)) Then 
            objCollection.DeleteMembershipRule Rule
            Wscript.Echo “Succesfully removed ” & sComputerName & ” from collection: ” & sCollectionID
        End If
    End If
Next

‘=============================
‘ Sub Routine to Connect to the SMS Provider
‘=============================
Sub ConnectToSMSProvider(SiteServerName)
   Set objSWbemLocator = CreateObject(“WbemScripting.SWbemLocator”)
   Set objSWbemServices = objSWbemLocator.ConnectServer(SiteServerName, “root\sms”)
   Set ProviderLocation = objSWbemServices.InstancesOf(“SMS_ProviderLocation”)
   For Each Location In ProviderLocation
      If Location.ProviderForLocalSite = True Then
         Set objSWbemServices = objSWbemLocator.ConnectServer(Location.Machine, “root\sms\site_” + Location.SiteCode)
      End If
   Next
End Sub

WScript.Quit(0)

ConfigMgr 2007 and changing the Package Source Directory by script

Sometimes there is a good reason to get out of your comfort zone. One of those reasons is moving the Source Directory of all packages to a different server/ share. This means there has to come a script to change the Source Directory of all packages, as it is not a job that you want to do manually, and scripting is not really my thing… But as it cost me some time to create something nice of it, I will share it so everyone can “enjoy it”.

I created three subroutines, one for connecting to the SMS Provider, one for changing the Package Source Path and one for change the Content Source Path. The Package Source Path counts for all the different types of packages and the Content Source Path only counts for Drivers. The only thing that needs to be adjusted is the old and the new location of the Package Source Directory. This script needs to be run from the Site Server.

Dim objSWbemServices

Set FSO = CreateObject (“Scripting.FileSystemObject”)
Set logFile = FSO.CreateTextFile (“PkgSourcePathLOG.csv”)

sOldServerShare = “<SERVER>\<SHARE>”
sNewServerShare = “<SERVER>\<SHARE>”
 
ConnectToSMSProvider()
ChangePkgSourcePath(“SMS_Package”)
ChangePkgSourcePath(“SMS_SoftwareUpdatesPackage”)
ChangePkgSourcePath(“SMS_ImagePackage”)
ChangePkgSourcePath(“SMS_OperatingSystemInstallPackage”)
ChangePkgSourcePath(“SMS_DriverPackage”)
ChangeContentSourcePath(“SMS_Driver”)

‘==================================
‘ Sub Routine to Connect to the SMS Provider
‘==================================
Sub ConnectToSMSProvider()
   Set objSWbemLocator = CreateObject(“WbemScripting.SWbemLocator”)
   Set objSWbemServices = objSWbemLocator.ConnectServer(“.”, “root\sms”)
   Set ProviderLocation = objSWbemServices.InstancesOf(“SMS_ProviderLocation”)
   For Each Location In ProviderLocation
      If Location.ProviderForLocalSite = True Then
         Set objSWbemServices = objSWbemLocator.ConnectServer(Location.Machine, “root\sms\site_” + Location.SiteCode)
      End If
   Next
End Sub

‘==================================
‘ Sub Routine to Change the Package Source Path
‘==================================
Sub ChangePkgSourcePath(strClass)
   Set colSWbemObjectSet = objSWbemServices.execQuery(“SELECT * FROM ” & strClass)
   For Each objSWbemObject In colSWbemObjectSet
      sOldPkgSourcePath = objSWbemObject.PkgSourcePath
      sNewPkgSourcePath = Replace(sOldPkgSourcePath, sOldServerShare, sNewServerShare)
      logFile.Write “Setting Package Source Path for ” & objSWbemObject.Name & ” from ” & sOldPkgSourcePath & ” to ” & sNewPkgSourcePath
      objSWbemObject.PkgSourcePath = sNewPkgSourcePath
      objSWbemObject.put_
   Next
End Sub

‘==================================
‘Sub Routine to Change the ContentSource of Drivers
‘==================================
Sub ChangeContentSourcePath(strClass)
   Set colSWbemObjectSet = objSWbemServices.execQuery(“SELECT * FROM ” & strClass)
   For Each objSWbemObject In colSWbemObjectSet
      sOldPathString = objSWbemObject.ContentSourcePath
      sNewPathString = Replace(sOldPathString, sOldServerShare, sNewServerShare)
      logFile.Write “Setting Content Source Path for ” & objSWbemObject.LocalizedDisplayName & ” from ” & sOldPathString & ” to ” & sNewPathString
      objSWbemObject.ContentSourcePath = sNewPathString
      objSWbemObject.put_
   Next
End Sub

Keep attention to the fact that changing the Source Directory will make the Distribution Point re-process the packages. This is the part that virtual application packages don’t like. They will generate lots of errors in the distrmgr.log and to fix it I had to touch them all and re-select the XML –file from the original project directory.