Follow-up on Microsoft Advisory ADV170012

If you are lucky and have System Center Configuration Manager, aka ConfigMgr, in you environment, you can get the inventory of the TPM embedded in you workstations or laptops.

You need to enable the Win32_TPM WMI class in the Hardware Inventory settings of your clients.

If you’ve only laptops, you can filter the query with the chassis types.
Then the problem is that the Manufacturer Id is returned as int32 and it doesn’t tell you what’s the manufacturer name and when the TPM manufacturer actually is Infineon whether its vulnerable or not.

Luckily, there’s a way to get the Manufacturer name from the int32 that is described on the the Win32_TPM WMI class on msdn.

Using the example provided, we can do

('{0:X0}' -f 1414548736) -split "(?<=\G.{2})",4 | 
ForEach-Object { 
 [char][int]"0x$($_)"
}

If I combine the ConfigMgr query, test if the TPM is vulnerable and get its manufacturer name from its id, I’ve the following code:

And if you use the 2nd example provided in the help, you can quickly have relevant results (IFX is Infineon)

If you cannot get results and have a WMI quota violation instead, see this post

Happy TPM madness scoping using #ConfigMgr and #PowerShell 😎

Advertisements

Quick tip: change ConfigMgr updates maximum run time

With the new servicing model being applied to pre-Windows 10 operating systems, it’s a good idea to change the default “maximum run time” of the monthly cumulative updates.
For example, I’ve:
configmgr-max-run-time

Here’s a way to achieve this easily with PowerShell.
The -Name parameter of the Get-CMSoftwareUpdate accepts wildcards although its documentation says the opposite.
The -Fast is explained in the following warning message:

WARNING: ‘Get-CMSoftwareUpdate’ supports -Fast for retrieving objects without loading lazy properties. Loading lazy properties can cause significant performance penalties. If it is not necessary to utilize the lazy properties in the returned object(s), -Fast should be used. This warning can be disabled by setting $CMPSSuppressFastNotUsedCheck = $true.

# 1. View what updates will be targeted:
Get-CMSoftwareUpdate -Fast -Name "*Security Monthly Quality Rollup for Windows*" |
Select Max*,*DisplayName
#NB: MaxExecutionTime is in seconds in this case

# 2. Set the maximum run time to 120 minutes
Get-CMSoftwareUpdate -Fast -Name "*Security Monthly Quality Rollup for Windows*"  | 
Set-CMSoftwareUpdate -MaximumExecutionMins 120 -Verbose

Trigger ConfigMgr client actions

A few weeks ago, I came across the following article on refreshing the System Center Configuration Management client

Whenever I see a script, I always wonder, could it be done with a oneliner ?
sms_client-actions-01

As long as the script doesn’t care about the order of the ConfigMgr actions to be performed, yes, it’s achievable this way:

(New-Object -COM 'CPApplet.CPAppletMgr').GetClientActions() | 
Where { 
    $_.Name -match "(\sPolicy$)|(Collection\sCycle$)|(Updates)"
} | ForEach-Object {
    try {
        $_.PerformAction()
        Write-Verbose -Message "Successfully executed $($_.Name)" -Verbose
    } catch {
        Write-Warning -Message "Failed to execute $($_.Name)"
    }
}

I’ve even added some filtering on action names and error handling.

sms_client-actions-02
There’s always more than one way to skin a cat with PowerShell 😉

Quick tip: subnet and ConfigMgr boundary for Direct Access clients

I’ve seen this morning the following blog post about boundaries in Configuration Manager for Direct Access clients.

I’d like to add more info on this topic because I’ve done the same in my environment a few days ago.

Gerry Hampson shows that he gets the ipv6 prefix directly in the properties of a Configuration Managment client in the ConfigMgr Admin console.
He made here an assumption. Having IPv6 addresses reported in Configuration Manager assumes that you have an IPv6 based DNS server where AAAA records are created for your Direct Access clients.
When you don’t have an IPv6 DNS server, you don’t have this info in the ConfigMgr client properties:

Where do I get the client ipv6 prefix ?

You can get it on your Direct Access server(s) with the following cmdlet

Get-RemoteAccess | select  ClientIPv6Prefix

Easy, isn’t it? 😎

Don’t forget! It may seem pretty obvious but…
Before adding the IPv6 prefix as a ConfigMgr boundary, add it first to your Active Directory Sites and Services subnets.
This way your Direct Access clients will immediately know with what Global Catalog and Domain controllers they should talk to.

LogParser vs. PowerShell

The following article popped-up this morning about How to extract NETBIOS name from BADMIF files on ConfigMgr 2012 using Log Parser 2.2

Hey, why would I need the old school log parser from 2005 when we have PowerShell nowadays and then another tool to manipulate the data output?

You can actually achieve this task with a PowerShell one-liner and even without having to manipulate the output.

Get-ChildItem -Path "C:\Program Files\Microsoft Configuration Manager\inboxes\auth\dataldr.box\BADMIFS" -Include *.MIF -Recurse -Force -ErrorAction SilentlyContinue | ForEach-Object { 
    try {
        (
            Get-Content -ReadCount 1 -TotalCount 6 -Path $_.FullName -ErrorAction Stop  | 
            Select-String -Pattern "//KeyAttribute<NetBIOS\sName><(?<ComputerName>.*)>" -ErrorAction Stop 
        ).Matches.Groups[-1].Value 
    } catch {
        Write-Warning -Message "Failed" 
    }
}

If you’ve got warnings, you may also want to investigate which files caused it.
In this case, I propose the following modification of the above code to be able to catch these files.

$ConfigMgrBoxPath = "C:\Program Files\Microsoft Configuration Manager\inboxes\auth\dataldr.box\BADMIFS"
Get-ChildItem -Path $ConfigMgrBoxPath -Include *.MIF -Recurse -Force -ErrorAction SilentlyContinue | ForEach-Object { 
    $File = $_.FullName ;
    try {
        (
            Get-Content -ReadCount 1 -TotalCount 6 -Path $_.FullName -ErrorAction Stop  | 
            Select-String -Pattern "//KeyAttribute<NetBIOS\sName><(?<ComputerName>.*)>" -ErrorAction Stop 
        ).Matches.Groups[-1].Value 
    } catch {
        Write-Warning -Message "Failed for $File" 
    }
}

PowerShell rocks, no doubt! 😎

WMI error 0x8004106C: Quota violation(while running queries)

I’ve encountered this error on my old System Center Configuration Manager 2007 while I was running intensive WMI queries.

The error message on quota violation is raised because the WMI provider of the operating system (Windows 2003) had its amount of private memory that can be held by each host set to a 128MB limit.

To fix this, I set to 512MB which is now the default on Windows 2008/2008R2/2012.

I think that at that time I followed the guidance that you can find in the following knowledge base article: KB2404366

To view the settings of the WMI provider, you can query the __ProviderHostQuotaConfiguration WMI class:

$WMIHT = @{            
 NameSpace=  'root'            
 Class = '__ProviderHostQuotaConfiguration'            
}            
Get-WmiObject -Computer SCCM2007 @WMIHT


Source: http://blogs.technet.com/b/askperf/archive/2008/09/16/memory-and-handle-quotas-in-the-wmi-provider-service.aspx

I’d recommend to stick to the piece of advice “do not modify these quotas for the sake of modifying them!” in the above link. In case you do, you can this way:

$WMIProviderConfig = Get-WmiObject @WMIHT            
$WMIProviderConfig.MemoryPerHost = 1024MB            
try {            
  $WMIProviderConfig.Put() | Out-Null            
  Write-Verbose -Message "Successfully changed the WMI provider settings" -Verbose            
} catch {            
  Write-Warning "Failed to modify the WMI provider because $($_.Exception.Message)"            
}

Note that a reboot is required.

Changing the ConfigMgr client health evaluator task (CCMEval)

My ConfigMgr 2012 book for training 10747A: Administering System Center 2012 Configuration Manager says on page 3-53 that:

This task runs ccmeval.exe at a time between 12:00AM and 1:00AM.

Ok, it’s fine to set a random time but not between midnight and 1:00AM…to avoid all computers sending info the Management Point at the same time.
Ok, the task is configured to run ASAP whenever a schedule is missed… for computers shut down at midnight and booting up in the morning.

If you’d like to introduce more randomness for this task, you can achieve it like this:

#Requires -Version 2

Function Set-CMCCMevalTaskTrigger {
<#
.SYNOPSIS
    Change the execution time of the CCMEval task

.DESCRIPTION
    Change the execution time of the CCMEval task by either setting a random start hour or a specified one

.PARAMETER ComputerName
    Array of string that represents the remote computers to target

.PARAMETER Hour
    Integer that sets the hour when the task will execute

.PARAMETER Random
    Switch to turn of a randomly chosen hour

.EXAMPLE
    Set-CMCCMevalTaskTrigger -Verbose
    Change to execution time on the local computer to 12:00AM instead of 12:00PM

.EXAMPLE
    "RemotePC1","RemotePC2" | Set-CMCCMevalTaskTrigger -Hour 13 -Verbose
    Change to execution time on the specified computers to 01:00PM

.EXAMPLE
    "RemotePC1","RemotePC2" | Set-CMCCMevalTaskTrigger -Random -Verbose
    Change to execution time on the specified computers to a random start time (hour)

#>
[CmdletBinding()]
Param(
[Parameter(ValueFromPipeline=$true,ValueFromPipeLineByPropertyName=$true)]
[Alias('CN','__Server','IPAddress','Server','hostname')]
[string[]]$ComputerName=$env:COMPUTERNAME,

[Parameter()]
[Alias('StartTime','Time')]
[ValidateRange(0,23)]
[int]$Hour = 12,

[Parameter()]
[switch]$Random

)
Begin{
    # Make sure we run as admin...
    $usercontext = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()
    # ...in international mode
    $IsAdmin = $usercontext.IsInRole(544)
    if (-not($IsAdmin)) {
        Write-Warning "Must run powerShell as Administrator to perform these WMI queries"
        break
    }
   
    $TASK_UPDATE_FLAG = 0x4

    if ($PSBoundParameters.ContainsKey('Random')) {
        $Hour = (Get-Random -Maximum 23 -Minimum 0)
        $NewTime = 'T{0:00}:' -f $Hour
    } else {
        $NewTime = 'T{0:00}:' -f $Hour
    }
}
Process {
    $ComputerName | ForEach-Object -Begin {
        $TaskService = New-Object -com schedule.service
    } -Process {
        $Computer = $_
        try {
            # Connect to target computer
            $TaskService.Connect($Computer)
            # Get the XML definition of the task
            $taskDef = $TaskService.GetFolder('\Microsoft\Configuration Manager').GetTask('Configuration Manager Health Evaluation').Definition
            # Loop through Calendar triggers to change the start time
            $taskDef.Triggers | ForEach-Object -Process {
                if ($_.StartBoundary) {
                    $_.StartBoundary = ($_.StartBoundary -replace "T\d{2}:",$NewTime)
                }
            }
            # Update the existing task
            $TaskService.GetFolder('\Microsoft\Configuration Manager').RegisterTaskDefinition(
              'Configuration Manager Health Evaluation',
              $taskDef,
              $TASK_UPDATE_FLAG,
              $null,
              $null,
              $taskDef.Principal.LogonType
            )
            Write-Verbose -Message "Successfully set CCMeval taks execution time to $Hour on $Computer"
        } catch {
            Write-Warning -Message "Failed to change CCMeval task for computer $Computer because $($_.Exception.Message)"
        }
    }
}
End {}
}