Tag Archives: ConfigMgr

ConfigMgr and “Who updated All Systems–again”?

As this questions shows up at all places for the same reason; Someone pressed the “Updated Membership” on a collection way up in hierarchy of collections. A simple right-click and press – and then accept.

image

The action is simple, however if the collection is ‘far up’ in the hierarchy and have all (or many) collections indirectly connected to it also start to update – an unforgiven workload. Delays, frustration and mayhem will ensue (perhaps a bit over the top here..)

If you are a type of organisation that blames people – well, here you go!

image

Choose to create a new Status Message Query and use the following query to filter the search based on time and only look for specific the refresh action of collections

Query;

select stat.*, ins.*, att1.*, stat.Time 
from SMS_StatusMessage as stat 
left join SMS_StatMsgInsStrings as ins on stat.RecordID = ins.RecordID 
left join SMS_StatMsgAttributes as att1 on stat.RecordID = att1.RecordID 
where stat.MessageType = 768 and 
stat.MessageID = 30104 and 
stat.Time >= ##PRM:SMS_StatusMessage.Time## 
order by stat.Time desc

image

End result – with the user account and collection to blame;

image

ConfigMgr, SQL Availability Groups and upgrades

If leveraging the new great features from SQL Server the last years – certainly the Availability Group have been a tempting way to increase availability for ConfigMgr (despite the fact that everyone prefers the local SQL Server running on the Primary Site server).  Now, as ConfigMgr TechPreview 1706 announced Site Server Role High Availability – separating the site server and the SQL database might be a new preferred method going forward in the future.

Microsoft provided a great step-by-step (2016-05-14) approach on all the steps required to migrate an existing database to a SQL Server Availability Group. As part of this a few specific SQL configurations are stated as a way to ensure an operational database;

We need to Set Trustworthy and enable CLR Integration. Run this against Primary Replica (You can confirm from the AlwaysOn dashboard)

Reviewing the official documentation (2017-05-26) there are however a few additional configurations detailed;

  • CLR Integration must be enabled
  • Max text repl size must be 2147483647
  • The database owner must be the SA account
  • TRUSTWORTY must be ON
  • Service Broker must be enabled

As a great benefit to that article there is a check for all the configuration options that clearly states wether they are correct or not. All of the above, but one, are very much a requirement for basic functionality. The Max text repl size was not stated in the step-by-step guide and is not a requirement for a functional database for normal operations, however during an upgrade it will be immediately detected as a deviation. What does ConfigMgr do with deviations? Well, it tries to fix it of course (don’t mention inbox backlogs or… or…).

An upgrade with settings configured according to the above requirements

image

An upgrade where on setting deviates from the requirements and the SQL database is part of an availability group;

image

The operation cannot be performed on database [CM_P01] because it is invovled in a database mirroring…
ALTER DATABASE [CM_P01] SET SINGLE_USER WITH ROLLBACK IMMEDIATE
Gives the response: Failed to set database [CM_P01] to SINGLE_USER mode

As ConfigMgr tries to fix the deviation it also tries to set the ConfigMgr database in SINGLE_USER mode, unfortunately that is not possible as long as the database is part of the Availability Group and mirrored. Only way is to configure according to the Microsoft recommend settings outside of ConfigMgr and then retry the upgrade.

ConfigMgr site restore and WSUS Catalog version

After you restore a ConfigMgr Primary Site Server there are some losses of information that gets annoying.

Sample; WSUS Catalog version is stored in the registry and the ConfigMgr database. It seems that the registry alone is enough to reset the used WSUS Catalog version, however Registry alone is not enough to restore the catalog version with ConfigMgr 1606.

Roger Zander described the behaviour and gave the right path, however some additional steps were required for ConfigMgr 1606.

Step 1. Identify the necessary catalog version that is required (see Roger Zanders previous description)

Step 2. Update the registry (see Roger Zanders previous description)

Step 3. Update the database. Locate the table dbo.Update_SyncStatus within the ConfigMgr database. Choose Edit Top 200 rows (and there – you are now unsupported by Microsoft).

image

Update the ContentVersion to match your Catalog Version

image

Step 4. Trigger a new “Synchronize Software Updates”

Copy a ConfigMgr Application DeploymentType

A small function inspired by Fred Bainbridges post on howto append an OS requirement to a deployment type. The purpose of the function is to copy the Deploymenttype within an application, but if someone feels like a spending a few hours to rewrite it to copy between different applications that could possible work aswell.

 

function Copy-CMAppDT {
<#
.SYNOPSIS
Copy a single Deployment Type within an application
.DESCRIPTION
This will create a copy of a DeploymentType, with the lowest priority and the name specified
.EXAMPLE
Copy-CMAppDT -appName "PingKing 2.0.0" -DeploymentType "PingKing 2.0.0" -newDTname "PingKing Updated" -siteCode P01 -siteServer CM01
.EXAMPLE
.PARAMETER appName
This is the name of the configmgr application that has the deployment type. This accepts input from pipeline.
.PARAMETER DeploymentType
This is the name of the Deployment Type that you want to copy.
.PARAMETER newDTName
This is the name of the new DeploymentType.
.PARAMETER siteCode
This the ConfigMgr site code you are working with. Defaults to LAB
.PARAMETER siteServer
This the site server you are going to working with.  WMI calls are made to this server.  It is most likely your primary site server.
#>
[CmdletBinding()]
param (
[Parameter(
Position=0,
Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)
]
$appName,
$DeploymentType,
$newDTname,
$siteCode = "LAB",
$siteServer = "cm01.cm.lab"
)
begin {
write-verbose "Import module"
import-module 'C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1' -force #make this work for you
write-verbose "Connect to Provider and change location"
if ((get-psdrive $sitecode -erroraction SilentlyContinue | measure).Count -ne 1) {
new-psdrive -Name $SiteCode -PSProvider "AdminUI.PS.Provider\CMSite" -Root $SiteServer
write-verbose "Connect to the default scope"
try {
$connectionManager = New-Object Microsoft.ConfigurationManagement.ManagementProvider.WqlQueryEngine.WqlConnectionManager
$connectionManager.Connect($siteServer) | Out-Null
[Microsoft.ConfigurationManagement.ApplicationManagement.NamedObject]::DefaultScope = [Microsoft.ConfigurationManagement.AdminConsole.AppManFoundation.ApplicationFactory]::GetAuthoringScope($connectionManager)
}
catch {
throw-error "$error[0]"
}
}
write-verbose "Set location $sitecode"
set-location $sitecode`:

}

process {
write-verbose "Get Application $appName"
try {
$Appdt = Get-CMApplication -Name $appName
}
catch {
throw "Unable to get $appName - $error[0]"
}

$xml = [Microsoft.ConfigurationManagement.ApplicationManagement.Serialization.SccmSerializer]::DeserializeFromString($appdt.SDMPackageXML,$True)

$numDTS = $xml.DeploymentTypes.count
write-verbose "Number of DT: $numDTS"
$dts = $xml.DeploymentTypes

foreach ($dt in $dts)
{
if ($dt.title -eq $DeploymentType ) {
write-verbose "Found DT $deploymenttype"
$newDeploymentType = $dt.Copy()
write-verbose "Set new DT name $newDTname"
$newDeploymentType.Title = $newDTname
$newDeploymentType.ChangeID()

}
}
if ($newDeploymentType.GetType().name -eq 'DeploymentType') {

write-verbose "New DT created"
$xml.DeploymentTypes.Add($newDeploymentType)

write-verbose "Commit to AppObject"
$UpdatedXML = [Microsoft.ConfigurationManagement.ApplicationManagement.Serialization.SccmSerializer]::SerializeToString($XML, $True)
$appdt.SDMPackageXML = $UpdatedXML
Set-CMApplication -InputObject $appDT
}
else {
write-error "No DeploymentType $newDTname located"
}
}

end
{
write-verbose "Return to c:"
set-location c:
}
}

ConfigMgr and a backlog in distributions

Scenario

Do you have a primary site and a few secondary sites in ConfigMgr 2012+?

Do you schedule the legacy Package format to update on a schedule?

image

Do you have a backlog in the distribution manager?

Well, so far this is known (by Microsoft) defect that apparently is yet to be fixed (until 1606 – nothing confirmed beyond that)

Symptoms

If you review the database where ConfigMgr resides you can see that there is a constant growing amount of DistributionJobs. Sample query to get an overview;

use <database>
select COUNT(*) from distributionjobs

The problem grows the more packages you have set to update on a schedule. The frequency of the schedule is not relevant, the package will loop into a forever updating loop. Most likely the primary site will handle this efficiently, however the sending to secondary sites will cause a backlog that is not just an annoyance but causing severe problems as the backlog will continue to grow.

Repeating this: The frequency of the schedule is not relevant. Just check the above checkbox and the issue will occur.

SQL query to locate relevant packages

use <database>
select pkg.PkgID, pkg.Manufacturer, pkg.Name, pkg.Version, pkg.Language, pkg.RefreshSchedule from SMSPackages as pkg
where datalength(pkg.RefreshSchedule) !=0

Fixit

Easy – uncheck all these check-boxes that updates packages. If you still want to update packages on a schedule use a powershell script to trigger the update and use the task scheduler to run the update.

Run the command-line;

powershell -executionpolicy bypass -file SCCM.UpdatePkg.ps1 -packageid <PACKAGEID>

Code:
(I honestly don’t know if I have stolen / copied this from somewhere – if I have give me a ping and I will remove this)

#========================================================================
# Created on: 2014-10-28 15:06
# Created by: Nicke Källén
# Organization: Applepie.se
# Filename: SCCM.UpdatePkg.ps1
#========================================================================
Param(
[Parameter(Mandatory=$True,Position=1)]
[string]$packageid
)

Function Invoke-CMPackageUpdate
{
[CmdLetBinding()]
Param(
[Parameter(Mandatory=$True,HelpMessage="Please Enter Primary Server Site code")]
$SiteCode,
[Parameter(Mandatory=$True,HelpMessage="Please Enter Primary Server Name")]
$SiteServer,
[Parameter(Mandatory=$True,HelpMessage="Please Enter Package/Application ID")]
$PackageID
)

Try{
$PackageClass = [wmiclass] "\\$($siteserver)\root\sms\site_$($sitecode):SMS_Package"
$newPackage = $PackageClass.CreateInstance()

$newPackage.PackageID = $PackageID

$newPackage.RefreshPkgSource()
}
Catch{
$_.Exception.Message
}

}

Invoke-CMPackageUpdate -SiteCode <SITECODE> -SiteServer <SERVER> -PackageID $packageid

Software Center can not be loaded

Regardless of what version of the ConfigMgr agent (2012 –> 1602) you get – there still seems to be a possibility to have left-overs from ConfigMgr 2007.

within the SCClient log-file the following error would be generated;

Exception Microsoft.SoftwareCenter.Client.Data.WmiException: Provider load failure       (Microsoft.SoftwareCenter.Client.SingleInstanceApplication at OnGetException)

The following is presented to the user when starting Software Center

image

Software Center can not be loaded. There is a problem loading the required components for Software Center.

It seems that this is due to a reference no longer in use – the dcmsdk.dll, located under SysWOW64 (on 32-bit systems). Sample output using reg query:

HKEY_LOCAL_MACHINE\Software\Wow6432node\classes\CLSID\{555B0C3E-41BB-4B8A-A8AE-8A9BEE761BDF}
(Default)    REG_SZ    Configmgr Desired Configuration WMI Provider

HKEY_LOCAL_MACHINE\Software\Wow6432node\classes\CLSID\{555B0C3E-41BB-4B8A-A8AE-8A9BEE761BDF}\InProcServer32
(Default)    REG_SZ    C:\WINDOWS\SysWOW64\CCM\dcmsdk.dll

End of search: 2 match(es) found.

Fix? Delete the registry key – sample command line;

reg delete HKLM\Software\Wow6432node\classes\CLSID\{555B0C3E-41BB-4B8A-A8AE-8A9BEE761BDF} /f

ConfigMgr: Match client address to IP-Range Boundaries

Despite the Microsoft recommendation, primarily due to additional workload that it causes, to not leverage IP-Ranges we have noticed a far greater significant accuracy of where clients retrieve content from based on our IP-ranges. So yes, we have our boundaries, with few exceptions, setup using IP-ranges.

We also have clients spread around the globe, new networks beeing spun-up, networks that aren’t supposed to be used for servers and clients and much more to actually be used for these type of things. The issue at hand is to understand where clients are actually connecting from, and what locations we know about.

To get some type of insight of where ConfigMgr clients are actually connecting from we started polling our database. In the end – this turned into two SQL-queries that would get all the IP-range boundaries, and a summary of how many clients we support on each network. As lazy as one can be – this ended up gathering enough information to present to other teams to present where clients are connecting from, how many there are and that we don’t previously didn’t know about this location.

To list how many clients you have per a /24-subnet. This may of course not necessarily be the exact size of a subnet, but it allows for an easy count-up of clients.

select SUBSTRING(ip.IPAddress0, 1, 
LEN(ip.IPAddress0) - CHARINDEX('.',REVERSE(ip.IPAddress0))) + ".1" As IP,
 COUNT(*) as Devices
 from v_Network_DATA_Serialized as ip 
where ip.IPAddress0 IS NOT NULL and ip.IPSubnet0 != "64"
and ip.DNSDomain0 like "%yourdomain.com"
and ip.TimeStamp > DATEADD(day, -10, GETDATE())
GROUP BY  SUBSTRING(ip.IPAddress0, 1, LEN(ip.IPAddress0) - CHARINDEX('.',REVERSE(ip.IPAddress0)))
ORDER BY Devices DESC

A list of all boundaries where we split the start and end IP-address of a specific range

select bound.DisplayName,
SUBSTRING(bound.value,1,CHARINDEX('-',bound.value) -1) AS LEFTHALF,
SUBSTRING(bound.value,CHARINDEX('-',bound.value) +1 ,100) AS RIGHTHALF
from vSMS_Boundary as bound
where bound.BoundaryType = "3"
and bound.DisplayName != "some boundary to exclude"

Information about the clients within a specific range that we do not know about

select DNSHostName0,
DNSDomain0,
IPAddress0,
IPSubnet0,
DefaultIPGateway0,
DHCPServer0
from v_Network_DATA_Serialized as ip
where ip.IPAddress0 IS NOT NULL
and ip.IPSubnet0 != '64'
and ip.DNSDomain0 like '%yourdomain.com'
and ip.TimeStamp > DATEADD(day, -10, GETDATE())
and ip.IPaddress0 like 'XXX.YYY.ZZZ.%'

 

To join all of this information together some basic, crude, logic was built in powershell to match up networks that clients are in and that we know about. The function to perform the actual IP-range lookup is from stackoverflow-reply. Sample output first:2015-11-22 16_07_41-Clipboard

 

 

 

function IsIpAddressInRange {
param(
 [string] $ipAddress,
 [string] $fromAddress,
 [string] $toAddress
 )

 $ip = [system.net.ipaddress]::Parse($ipAddress).GetAddressBytes()
 [array]::Reverse($ip)
 $ip = [system.BitConverter]::ToUInt32($ip, 0)

 $from = [system.net.ipaddress]::Parse($fromAddress).GetAddressBytes()
 [array]::Reverse($from)
 $from = [system.BitConverter]::ToUInt32($from, 0)

 $to = [system.net.ipaddress]::Parse($toAddress).GetAddressBytes()
 [array]::Reverse($to)
 $to = [system.BitConverter]::ToUInt32($to, 0)

 $from -le $ip -and $ip -le $to
}


$ErrorActionPreference = "silentlycontinue"
$database = "ConfigMgrServer"
$datasource = "ConfigMgrDB"

$netquery = "select SUBSTRING(ip.IPAddress0, 1, LEN(ip.IPAddress0) - CHARINDEX('.',REVERSE(ip.IPAddress0))) + '.1' As IP, COUNT(*) as Devices from v_Network_DATA_Serialized as ip where ip.IPAddress0 IS NOT NULL and ip.IPSubnet0 != '64' and ip.DNSDomain0 like '%yourdomain.com' and ip.TimeStamp > DATEADD(day, -10, GETDATE()) GROUP BY SUBSTRING(ip.IPAddress0, 1, LEN(ip.IPAddress0) - CHARINDEX('.',REVERSE(ip.IPAddress0))) ORDER BY Devices DESC"

$networks= Invoke-Sqlcmd -Query $netquery -server $datasource -Database $database

$query = "select bound.DisplayName, SUBSTRING(bound.value,1,CHARINDEX('-',bound.value) -1) AS LEFTHALF,SUBSTRING(bound.value,CHARINDEX('-',bound.value) +1 ,100) AS RIGHTHALF from vSMS_Boundary as bound where bound.BoundaryType = '3' and bound.DisplayName != 'exclusion boundary'"

$iprange = Invoke-Sqlcmd -Query $query -server $datasource -Database $database

foreach ($net in $networks) {
 if (!($net.ip -eq '192.168.1.1')) {
 $i = 0
 $J = $iprange.count
 $boundaryfound = $false
 do {
 if (IsIpAddressInRange $net.ip $iprange[$i].LEFTHALF $iprange[$i].RIGHTHALF)
 {

 $boundaryfound = $true
 }
 $i++
 } until ($i -gt $j)
 if ($boundaryfound -eq $false) 
 {
 write-host "Network: $($net.ip) - Devices: $($net.Devices)"

 #$($($net.ip) -replace ".$")
 $devquery = "select DNSHostName0,DNSDomain0,IPAddress0,IPSubnet0,DefaultIPGateway0,DHCPServer0 from v_Network_DATA_Serialized as ip
 where ip.IPAddress0 IS NOT NULL
 and ip.IPSubnet0 != '64'
 and ip.DNSDomain0 like '%yourdomain.com'
 and ip.TimeStamp > DATEADD(day, -10, GETDATE())
 and ip.IPaddress0 like '$($($net.ip) -replace ".$")%'"
 $devices= Invoke-Sqlcmd -Query $devquery -server $datasource -Database $database
 $devices
 

 }
 }
}

ConfigMgr client–install and maintenance

After going down a rabbit hole attempting to understand the process of why ConfigMgr clients fail. Fail to install, fail to keep themselves running, failure to repair themselves – there has been a realization that a lot of people have spent an insane amount of time doing the same thing. Oddly enough, very few people seem to understand the implications of doing things one way or another.

Hopefully this can be a summary post that will detail the implications of choosing different paths when installing the ConfigMgr client.

Prerequisites

There is an extensive documentation of what the technical requirements are of the platform you intend to install the ConfigMgr client on. Most likely this will not be any major issue as the majority of these will be fulfilled on any supported platform and if they are not available prerequisite will be installed as part of the very stable CCMSetup.exe.

ConfigMgr setup (called ccmsetup.exe) is a wrapper executable that will leverage the contents of ccmsetup.cab. The .CAB-file contains a XML file that will detail all prerequisite checks that should be performed on a client and attempt to verify that they are installed, and if not download them (from a source-repository defined in many ways – if none is specified the folder which ccmsetup is started from will be the source)  and install them.

Can this cause issues? Yes. If the folder where ccmsetup.exe is called from does not contain the prerequisites specified in ccmsetup.cab so they actually can’t be downloaded or the alternative source-folders specified doesn’t contain them the installation will fail. Or worse, if the requirements are invalid this will also cause issues. An invalid hash for SCEPInstall.exe was the culprit of an update released by Microsoft or the publically acknowledged MicrosoftPolicyPlatformSetup.msi invalid signature has also been the cause of headaches.

So lets clarify;

Don’t remove things from the ConfigMgr client source-folder.

image

Installation

Interaction

When ccmsetup.exe is started it is a non-interactive process which only allows anyone to review the progress by reading log-files. For the impatient that hopes to have a progress-bar running – there is none. (log files located in c:\windows\ccmsetup\logs)

CCMSetup.exe has some intelligence to avoid have someone firing of the non-interactive process and fail to properly install the ConfigMgr client. It uses the Active Directory (in multiple ways) to identify most of the settings that are required, however of course there are many optional and beneficial options.

Install Properties

There is a small rule when it comes to passing parameters to ccmsetup.exe
Parameters that start with a / is dedicated for the ccmsetup.exe-wrapper itself, whereas the parameters without (and usually written in capital letters) are dedicated for the MSI and used by the installation process. Parameters meant for the ccmsetup.exe (with /) should be written first and before all parameters meant for the client MSI.

So, the client will do the following to identify how it should be installed:

Parse commandline

Ccmsetup command line: "C:\WINDOWS\ccmsetup\ccmsetup.exe" /runservice "SMSCONFIGSOURCE=P" "smssitecode=P01"

Read from registry

Command line parameters for ccmsetup have been specified.  No registry lookup for command line parameters is required.

Query Active Directory

Performing AD query: '(&(ObjectCategory=mSSMSManagementPoint)(mSSMSDefaultMP=TRUE)(mSSMSSiteCode=P01))'

Parameters published within Active Directory is defined from the ConfigMgr environment when modifying the Site-settings

image

image

So, as long as you have a fairly uncomplex environment of ConfigMgr the installation of the ConfigMgr client will be simple and you can pass the installation files to anyone without a lot of instructions. Just double-click this file (ccmsetup.exe) and the settings will be retrieved from Active Directory, at minimum.

Download of installation-files

If there are multiple offices and perhaps even some limited bandwidth scenarios with high-latency a quick question comes to mind – how can we optimize this?

As previously noted the most common scenario is that the client itself and the prerequisites will download themselves from the folder they were started into a local-cache folder named ccmsetup. Technically, the ccmsetup will just perform enough actions to get this download started and then handover to the local process that will continue to download the of remaining prerequisites and client-files as illustrated by the log-file.

image

Once the local file exists (along with ccmsetup.cab) the real (now local) installation-process will continue the installation. This will continue to download the remaining bits and pieces from either the source-folder or an alternative source-folder:

/source:<Path>

There can be multiple source-folders specified and before it attempts to use one the ccmsetup.exe will verify that the source-folder can be reached – if not it will invalidate it.

Technically, the commandline can look like this

ccmsetup.exe /source:\\validfileshare\client<em> </em>/source:<em>\\invalidfileshare\client </em>/source:<em>\\validfileshare2\client

 

The folder used will be based on availability (must be reachable) and the order in which they were listed. There is no intelligence that calculates latency or bandwidth, the only check performed is if the files are reachable. However, if you want that to be used you can leverage the ConfigMgr infrastructure using the property /mp.

/mp:<em><Computer>

MP stands for Management Point and this specific property will only define what Management Point CCMSETUP will contact to retrieve the client source-files. The property will not be used by the ConfigMgr client itself (you can use a different property for this). The Management Point will leverage the ConfigMgr package created during its setup (called Configuration Manager Client Package) and all of the ConfigMgr infrastructure. Based on the boundaries defined and what Distribution Point will serve those boundaries it will download the package from the best local (read Fast boundary) Distribution Point, and if none are available it will fallback to a remote one (read slow).

Sample review of the earlier stages of an CCMSetup running where it is contacting the Management Point for content download.

image

The download from a Distribution Point will leverage BITS and the priority of that download can be specified using the BITS priority property.

/BITSPriority:<em><Priority>

 

Patches

ConfigMgr is a living product and therefore there are lots of released updates for it. ConfigMgr 2007 had a set of specific solutions (hotfixes) released that included updates to the client and every once in a while these were gathered up in a Service Pack or Release Pack (SP or R) which provided a brand new MSI-file.ConfigMgr 2012 still has the concept of Service Pack and Release Pack, however very few hotfixes have been released in between these releases and instead they are release in cumulative packs that will provide a new MSP-file with all the fixes from backwards.

Great improvement that ConfigMgr 2012 has a single Microsoft Patch to be installed. Of course there is a desire to ensure that the clients receives these fixes as soon as possible and if possible to ensure that at installation-time the patch is applied. This is especially important during Operating System Deployment as the fixes may resolve issues during the Operating System Deployment process.

Patches introduces the first challenge when it comes to ensuring that a client will install properly.

Supported method

Microsoft has recommended the approach of using the generic Windows Installer-property PATCH. This has been around as a recommended method since SMS 2003, and continue to be the official recommended way forward.

There are a few caveats to using the PATCH property and that is namely the limitation of referencing any type of file there.

File path has to be an absolute path and no variables can be used when the command-line is parsed by Windows Installer. If the file referenced does not have the extension MSP it will be ignored. In essence: You have to write the specific path where the file is located in plain old english (or.. well..)

Microsoft has therefore provided great blog-articles (no technet-article as the property is a generic Windows Installer property) on howto leverage this for the published Client Installer Properties and multiple ways of using this property during the Operating System Deployment process.

Applying a ConfigMgr Hotfix During the Client Installation of an OS Deployment

Patch ConfigMgr 2012 x86 and x64 clients during a task sequence using the PATCH property

Deploying ConfigMgr Hotfixes

There is one key take away that is worth noting and that is the fact that using the PATCH property will install all the bits of the new patch during the initial installation of the client and once the installation is completed the client will be at the latest build of the client. If applying the patch post-installation it is required to restart the ConfigMgr client (we want to release any locks to a file) before we have actually applied the update.

Primary concept here are based into a few parts that will provide us with a few different ways of populating the PATCH property depending on when and how we install the MSP.

Install on existing Windows machines

When using the published installer properties (used for client push scenarios, manually triggered installs without any overriding options) we will reference the patch using a UNC-path. The UNC-path can reference any file-share that the computer account has access to. Sample PATCH-property

PATCH=\\Siteserver\SMS_CNT\client\x64\hotfix\KB2905002\Configmgr2012ac-r2-kb2905002-x64.msp

Using a UNC-path of course is a hard-coded reference to a specific file-share (and using DFS you can of course offer some abstraction here), however there is a concern to be raised with this method.

Referencing a UNC-path would of course require that the client has access to the MSP-file on the occasion that a future repair would happen or during the installation. If the file would not be reachable (move, network disconnect, permissions improperly configured) the installer with exit with the generic exit code of 1635. Even worse though is the complete failure of the installation due to a high-latency / low bandwidth scenario where the file-copy operation just fails. Sample error code in such a scenario:

MSI: Action 1:44:18: InstallFiles. Copying new files    ccmsetup    2015-08-02 01:44:18    8560 (0x2170)
MSI: Internal Error 2902. ixfAssemblyCopy    ccmsetup    2015-08-02 01:44:20    8560 (0x2170)
MSI: Action 1:44:21: Rollback. Rolling back action:    ccmsetup    2015-08-02 01:44:21    8560 (0x2170)

Install during Operating System Deployment

During OSD there seems to be two ways to reference the patches. The previous (circa 2007 and a bit of the life-span of 2012 in) way was to reference the patches in their respective packages when downloaded by ConfigMgr during the OSD-process. A sample path has been expressed in the first article. The PATCH would reference a folder, temporarily, created by a specific package and could install the patch with local access.

PATCH="C:\_SMSTaskSequence\OSD\XXX00002\x64\hotfix\KB2905002\Configmgr2012ac-r2-kb2905002-x64.msp" 

During later articles there is a new recommended method of running a pre-step to copy the file into a local-folder (c:\windows\temp anyone?) and then referencing this folder instead

PATCH=C:\windows\temp\SCCMHotfix\configmgr2012ac-r2-kb2910552.msp
Summary

Briefly looking over these methods we have a few things worth noting;

1. We do not have the ability to leverage a single way of referencing these patches if we were to follow Microsoft practices. UNC-paths for one method and a local-copy for a different method?

2. We can not leverage the ConfigMgr infrastructure when downloading patches since we are forced to reference them using a hardcoded path during a manual install / push-scenario

3. Patch installation from a UNC-path may be high-risk as any loss of network connectivity or if the connection is less than perfect may result in a complete failure of the client install. Preferred method (which is most likely this is the new recommended approach for OSD) would be to download a copy to the local harddrive on the client.

4. There is no dynamic way of placing the patches in a single location and have the ConfigMgr client automatically apply them. We have a hardcoded reference to the patch location, and in addition we also have a hardcoded reference to what patch we are deploying.

Now, remember all these cons as more will be remarked later.

Unsupported method

Since SMS 2003 there has been some legacy code that will allow the CCMSetup to detect a specific folder, download any patches located in this folder and then post installation also install the patches.

The folder is called ClientPatch (surprise!) and should be created in each architecture directory.

image

Once the installation is started and detects that this folder is located in the source-folder it is currently using it will download the file

image

image

After the installation of client.msi is completed it will continue to install any downloaded MSP-files.

image

image

This method is unsupported and there is no testing performed by the ConfigMgr team if any changes they make will potentially cause any future issues. There is a lot of public ‘it works without issues’ and some people saying ‘we have identified issues’.

People outside of Microsoft claims that it´operates without issues.

SCCM 2012 Client Push – Including hotfixes / CU patches in Client Push

System Center 2012 Configuration Manager R2 Automating Client Patch installation during Client Push Install with CU2

ClientPatch folder method for including client updates – supported?

Change in how to apply Patch with SCCM Client push

GOOD INFO: HOW TO AUTOMATICALLY APPLY A CLIENT HOTFIX AS PART OF A CLIENT PUSH OR CLIENT INSTALLATION

Summary

Issues that have been identified relate to specific properties not beeing parsed properly is using this approach as the installation order is to setup the client, and then after the client is installed it will automatically install the patch. As the patch installation must stop all services (think ConfigMgr agent, but also the Task Sequence service) to release any locks to files that it wants to update the loss of properties seems harmless (and yet also a logical side-effect) in comparison.

As you might guess; Stopping the Task Sequence service during Operating System Deployment is a high-risk undertaking.

 

Maintenance

Configuration Manager has introduced several mechanisms to ensure a ConfigMgr client stays healthy. One part of this, which has been remarked early on, is the CCMEval scheduled task.

image

The CCMEval performs two tasks namely to identify (task one) and remediate (task two) unhealthy clients. The name is a give-away. There is an option to configure it to only perform the first (either through a property at install-time or as a post-configuration task) which of course will only notify you as an administrator through the ConfigMgr console. And by notify we mean that you have to actively dig out the information under Monitoring –> Client Status.

image

CCMEval’s doings (or undoings) can be identified through the log-file named ccmsetup-ccmeval.log located in C:\Windows\ccmsetup\Logs.

Identify task

Lets run through its doings when performing a health check to identify a unhealthy client

First we can see that it triggers the ccmsetup with the parameter /evaluate and focusing only on prerequisites. Most likely this will identify that all necessary prerequisites are installed, available for installation if they are missing and if not it should trigger an installation of the missing prerequisites.

As you can see it remembers the command-line we used for installing the client. A validation will also happen (just like it does during install-time) that source-folders are validated.

image

The previously used command-line for installation is saved away in the registry. As far as the investigation goes this little vital component is saved in the below registry-key.

image

Once it has validated the source the client knows about (remember; working folder, /source and /mp are valid options to specify this during the initial installation) it will start to digest the information that it knowns about the prerequisites. Just as the initial installation it will extract the XML-file (now using the local-copy) from ccmsetup.cab and then proceed to ensure what prerequisites are applicable, available and installed.

image

Once the prerequisites had their run there is an secondary run that will validate the client itself

image

The result of this (and what checks are performed to ensure that a healthy ConfigMgr client is on the machine) is logged in a secondary file located in C:\Windows\CCM\Logs and its named (surprise!) ccmeval.log

image

 

Remediate Task

The above portion will only identify the current state of a client and if configured as such – the CCMeval will stop at the above part and inform (notify) the ConfigMgr hierarchy that this is the case. By default it is also enabled to remediate a client which is considered unhealthy.

One of the more harmless remediation is to just start the CCMExec-service. Fairly easy right? Serivce not running? Start it.

One of the more challenging remediation is to reinstall either the prerequisites or even the client in itself.

Wait. What? why would this be a challenge? Lets play out two potential scenarios that are very likely to happen if following some articles that have been posted above.

Scenario #1

You followed the suggestion to ensure that the ConfigMgr 2012 hotfixes are installed during the Task Sequence that deploy the operating system using the package that contains the ConfigMgr Client. A sample PATCH-property would be seen like this:

PATCH="C:\_SMSTaskSequence\OSD\XXX00002\x64\hotfix\KB2905002\Configmgr2012ac-r2-kb2905002-x64.msp" 

As this is a temporary location for storing packages only during the Task Sequence execution once a Task Sequence has completed the folder is removed.
If the client were to become faulty due to <insert any reason> a repair would be attempted using the initial command-line as found in the registry (HKLM\Software\Microsoft\CCMSetup) such as this

"/runservice" "/source:\\fileshare\validsource" "SMSCONFIGSOURCE=P" "smssitecode=P01" "FSP=fsp.domain.com" "PATCH="C:\_SMSTaskSequence\OSD\XXX00002\x64\hotfix\KB2905002\Configmgr2012ac-r2-kb2905002-x64.msp"  "

However, this installation will fail with a generic error code of 1635 due to the installer being unable to locate the previously temporary folder. The patch is no longer in the above location.

Scenario #2

During a client push scenario we have published the UNC-path where our patch-files are located on a file-share within our environment. This could be on the site-server, a DFS-root or somewhere else.

Sample property

PATCH=\\Siteserver\SMS_CNT\client\x64\hotfix\KB2905002\Configmgr2012ac-r2-kb2905002-x64.msp

There are two potential concerns that may cause harm here if a repair ever were to be triggered. If an admin was haggled to cleanup, save disk space or perform any similar action a year later a scenario will arise where the patch is not available. Perhaps some a new way of distributing the hotfix was discovered and it was decided to cleanup old file-shares. Well, if the above patch was no longer available at the above location all future repairs would halt with a generic 1635 error.

The other concern raised is if the file is still available however the client is constrained by high latency. Perhaps some packet drops. Well, perhaps the connection might be congested with Channel9 video streams. During the patch file-copy part the following error is very likely to happen

MSI: Action 1:44:18: InstallFiles. Copying new files    ccmsetup    2015-08-02 01:44:18    8560 (0x2170)
MSI: Internal Error 2902. ixfAssemblyCopy    ccmsetup    2015-08-02 01:44:20    8560 (0x2170)
MSI: Action 1:44:21: Rollback. Rolling back action:    ccmsetup    2015-08-02 01:44:21    8560 (0x2170)

Summary

In the two above scenarios we will end-up with a completely unhealthy ConfigMgr client.

This is why a suggestion has been created to combine the PATCH property method of installing patches and the ClientPatch-concept into a single new way forward of deploying patches.

Go vote!

ConfigMgr client install – Error 0x80070008. File may be corrupt

While troubleshooting the failure of the ConfigMgr 2012 R2 Client to install on a generic Windows 7 x64 installation on a machine we spent numerous hours digesting the following error code that refused the completion of the installation in the CCMSETUP.log file

Failed extract manifest file from cab file 'C:\WINDOWS\ccmsetup\ccmsetup.cab'. Error 0x80070008.  File may be corrupt.

In the end it seemed that the inkling that the ccmsetup.cab was corrupted was false (surprise!), however the client did have issues extracting the manifest from it and so the installation halted.

Looking up the error code a generic answer is given:

Not enough storage is available to process this command.

Source: Windows

The error code immediately leads to a KB-article by Microsoft (and written in similiar style by many other vendors and bloggers) that would hint towards manipulating the IRPStackSize registry key for older (Windows 2000..) versions of Windows to resolve the issue. Unfortunately Windows 7 will not be so easily tamed.

After reviewing the Process Monitor logs one idea came that it utilized the CABINET.DLL in C:\Windows\System32 to extract the information. Review the details tab revealed this;

 

image

A client without the issues gave us these properties:

clip_image002

 

Most likely a execution of SFC /SCANNOW might have resolved the issue, however since we knew the file we were interested in it was simply replaced with a known working version from a different machine.

Redistribute Failed Packages in ConfigMgr

Since the topic of redistributing failed packages is quite often surfacing in larger environments and there are quite a few PowerShell scripts out there to achieve this.

David O´Brien has written a PowerShell script that redistributes all packages that has any state (but successfull) to all DPs. In a larger environment this would be very risky (consider the amount of bandwidth you could potentially consume).

David went about the task by looking up the current state of the SMS_PackageStatusDistPointsSummarizer which has 7 states of a package , and then looping through all packages for all DPs and initiate the operation RefreshNow for each package and DP.

Within SCCM 2012 R2 there seems to be 9 possible states of a package, where a state 7 and 8 seems to be undocumented. State 7 would indicate that the source-files were not reachable for the SCCM 2012 server, and State 8 would indicate that a package validation failed (for any reason).

Quite often the need is more targeted and in particular we are required to only verify a single package or distribution point. As we would go through the console to check the state of a package and look under Content Status to see – it would be easiest to simply trigger a redistribute action for all DPs that are reported as failed. Previously Greg Ramsey released the great tool to start the action Validate All DPs, which can be initiated from any package under Content Status. Great tool! Lets take that one step further and create two additional menus within Configuration Manager console!

Redistribute a package to all DPs where it failed under Content Status

image

image


Param(
[Parameter(Mandatory=$true)]
[String]$SiteSrv,
[Parameter(Mandatory=$false)]
[String]$SiteNamespace,
[Parameter(Mandatory=$True)]
[String]$PackageID
)

$SiteCode = $SiteNamespace.Substring(14)

Write-Host "Checking" $PackageID -ForegroundColor red
Write-host "Will check for Installed Failed status (3) and Validation Failed (8)"
Write-Host ""

$sOpt = New-CimSessionOption –Protocol DCOM
$SiteServerCIM = New-CimSession -ComputerName $SiteSrv -SessionOption $sOpt

$DPs =  Get-CimInstance -CimSession $SiteServerCIM -Namespace $SiteNamespace -ClassName SMS_PackageStatusDistPointsSummarizer -Filter "PackageID = '$($PackageID)' AND (State = '3' OR State = '8')"
if ($DPs -ne $null)
{
$Count = $DPs |measure
Write-Host "There are $($Count.count) failed DPs at the moment."
Write-Host "Press any key to redistribute..."
Write-Host ""
$a = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
foreach ($dp in $DPs) {
Write-Host "Redistributing $($DP.PackageID) to $($DP.ServerNALPath.Substring(12,7))...." -ForegroundColor Green
try {

Get-CimInstance -CimSession $SiteServerCIM -Namespace $SiteNamespace -ClassName SMS_DistributionPoint -Filter "PackageID='$($DP.PackageID)' and ServerNALPath like '%$($DP.ServerNALPath.Substring(12,7))%'" | Set-CimInstance -Property @{RefreshNow = $true}
}
catch {
Write-Host "Failed to redistribute to $($DP.ServerNALPath.Substring(12,7))" -ForegroundColor Red
}

}
Write-Host "Press any key to close window...."
$a = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}
else
{
Write-Host "There are no Failed DPs" -ForegroundColor Green
Write-Host "Press any key to close..."
$a = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

}

Remove-CimSession -CimSession $SiteServerCIM 

Create the following XML-file to enable the right-click menu under Content Status. The file should be placed in the following folder;

14214306-59f0-46cf-b453-a649f2a249e1


<ActionDescription Class="Executable" DisplayName="Redistribute to all failed DPs" MnemonicDisplayName="Redistribute to all failed DPs" Description = "Redistribute to all failed DPs" RibbonDisplayType="TextAndSmallImage">
<ShowOn>
<string>ContextMenu</string>
<string>DefaultHomeTab</string>
</ShowOn>
<Executable>
<FilePath>PowerShell.exe</FilePath>
<Parameters>-Executionpolicy bypass -nologo -WindowStyle normal -command "&amp; 'C:\PowerShellScripts\RedistFailed.ps1' '##SUB:__Server##' '##SUB:__Namespace##' '##SUB:PackageID##'" </Parameters>
</Executable>
</ActionDescription>

To enable a second menu under Distribution Point Configuration Status you can use the following script;

image

image


Param(
[Parameter(Mandatory=$true)]
[String]$SiteSrv,
[Parameter(Mandatory=$false)]
[String]$SiteNamespace,
[Parameter(Mandatory=$True)]
[String]$Server
)

$SiteCode = $SiteNamespace.Substring(14)

Write-Host "Checking" $Server -ForegroundColor red
Write-host "Will check for all failed packages (NOT State 0)"
Write-Host ""

$sOpt = New-CimSessionOption –Protocol DCOM
$SiteServerCIM = New-CimSession -ComputerName $SiteSrv -SessionOption $sOpt
try {
$pkgs =  Get-CimInstance -CimSession $SiteServerCIM -Namespace $SiteNamespace -ClassName SMS_PackageStatusDistPointsSummarizer -Filter "ServerNALPath like '%$($Server)%' AND (State != '0')"

if ($pkgs -ne $null)
{
$Count = $pkgs |measure
Write-Host "There are $($Count.count) failed packages at the moment."
Write-Host "Press any key to redistribute..."
Write-Host ""
$a = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
foreach ($pkg in $pkgs) {
Write-Host "Redistributing $($pkg.PackageID) to $($Server)...." -ForegroundColor Green
try {
Get-CimInstance -CimSession $SiteServerCIM -Namespace $SiteNamespace -ClassName SMS_DistributionPoint -Filter "PackageID='$($pkg.PackageID)' and ServerNALPath like '%$($Server)%'" | Set-CimInstance -Property @{RefreshNow = $true}
}
catch {
Write-Host "Failed to redistribute to $($pkg.ServerNALPath.Substring(12,7))" -ForegroundColor Red
}

}
Write-Host "Press any key to close window...."
$a = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}
else
{
Write-Host "There are no Failed pkgs" -ForegroundColor Green
Write-Host "Press any key to close..."
$a = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

}

Remove-CimSession -CimSession $SiteServerCIM

}
Catch {
Write-Error "Failed to query $($Sitesrv)"
Remove-CimSession -CimSession $SiteServerCIM
} 

To enable the right-click menu, create a new XML-file under the following folder;

d8718784-99d5-4449-bc28-a26631fafc07

Content;

<ActionDescription Class="Executable" DisplayName="Redistribute all failed Pkgs" MnemonicDisplayName="Redistribute all failed Pkgs" Description = "Redistribute all failed Pkgs" RibbonDisplayType="TextAndSmallImage">
<ShowOn>
<string>ContextMenu</string>
<string>DefaultHomeTab</string>
</ShowOn>
<Executable>
<FilePath>PowerShell.exe</FilePath>
<Parameters>-Executionpolicy bypass -nologo -WindowStyle normal -command "&amp; 'C:\PowerShellScripts\redistfailedpkgstodp.ps1' '##SUB:__Server##' '##SUB:__Namespace##' '##SUB:NAME##'" </Parameters>
</Executable>
</ActionDescription> 

You can download the scripts from here, but you need to copy the XML-files into the folder of the Admin-Console yourself to make them visible.

Location;

c:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\XmlStorage\Extensions\Actions