Defender for Endpoint – Whats the user count?

Whats the user count for users actually logged onto your devices – looking through Defender For Endpoint?

Quick-glance;

DeviceLogonEvents
| where AccountDomain == "YOURDOMAIN"
| where LogonType in ("Interactive","CachedInteractive") and ActionType == "LogonSuccess"
| extend parsed = parse_json(AdditionalFields)
| extend Localcheck = tostring(parsed.IsLocalLogon)
| where Localcheck notcontains "false"
| summarize AccountName=dcount(AccountName) by AccountDomain

CMPivot and SMB1

Ned Pyle has ensured there is a Event-log that details any attempts to communicate with SMB1 (incase this still is enabled on your endpoint). It exists both for SMBServer and SMBClient

See his great post for specifics regarding the event;

As of Configuration Manager (or MECM) 1910 you can utilize CMPivot to query all Event-logs (previously only a subset where available is only the Get-WinEventLog cmdl:et was used) – including SMBClient/Audit.

Sample query – summarized the number of events 30 days backwards per client

WinEvent('Microsoft-Windows-SmbClient/Audit', 30d) 
| where ID == 32002
| summarize count() by Device

Sample query – device, date and message

WinEvent('Microsoft-Windows-SmbClient/Audit', 30d) 
| where ID == 32002
| project device, datetime, Message

In addition you can create a collection of the clients you found;

Or if it needs to be pretty;

WinEvent('Microsoft-Windows-SmbClient/Audit', 30d) 
| where ID == 32002
| summarize count() by Device 
| render barchart with (kind=stacked, title='SMB1 Events', ytitle='Events')

Troubleshoot Office crashes – quick guide

Wrote these notes on how so many Office issues were solved. Sadly – this still applies – and a recent thread from Twitter reminded me that it might be useful.

Printer

Temporarily set the printer to ‘PDFCreator’ as default printer

Verify if the issue is resolved

Addins

Start the application in safemode or without addins

Sample:

winword.exe /a

excel.exe /s

outlook.exe /safe

If the application doesn’t crash if the Office application is started without addins, verify what addins the user has installed.

Narrow down the issue and attempt to identify which addin is causing the crash

You can review installed addins by selecting;

File -> Options

Review the Addins-option

Temporarily disable addins by using the Manage -> Go.. at the bottom

Some addins may require that you temporarily start the application as Administrator.

Profile

Close all Office applications

Registry issues

Try to temporarily rename the settings for a specific application in registry

Open regedit.exe

Locate the;

HKEY_CURRENT_USER\Software\Microsoft\office

Locate the crashing application;

Word, Excel, PowerPoint

Sample Path;

HKEY_CURRENT_USER\Software\Microsoft\Office\Word

Rename the registry key for your application – using Word as a sample;

word_temp 

Retry to start the application

Repeat the same steps for the specific application

HKEY_CURRENT_USER\Software\Microsoft\Office

Locate the specific version;

12.0 -> 2007

15.0 -> 2013

16.0 -> 2016

If the issue is not resolved, rename the version registry key – using 12.0 as a sample;

12.0_temp

If the issue is not resolved by temporarily renaming registry keys it is recommended to restore all registry-keys to their original name

Files

You can temporarily rename application specific folders for Office-applications. Suggestion is to rename the below folders to _temp and verify if the issue is resolved

%APPDATA%\Microsoft\Word

%APPDATA%\Microsoft\Excel

%APPDATA%\Microsoft\PowerPoint

%APPDATA%\Microsoft\Templates

%APPDATA%\Microsoft\Outlook

NTUser.dat and last updated

Regardless what type of estate of Windows-devices, there always seems to be a need of clearing out unused profiles from a computer to save diskspace, increase performance and what not.

In Windows 7 there was an issue (resolved by a hotfix) that simply loading up a ntuser.dat file would change the timestamp of when it was last written to. It seems that this has now been the defacto default behaviour for Windows 10, and a long-running thread disusses different ways of adressing the issue – how can you identify if a profile was recently used on a device? Nirsoft tools (aren’t they great?) provide a great and easy to read overview if logon history based on security event logs. 

That seems tedious. Using the written time for the folder doesn’t seem to be accurate – and the risk of removing active user profiles is high. However, if one could track the last-write time for the registry entry for the profile – we should be good, right? Unfortunately – last write time for the registry entry isn’t there out of the box using Powershell (or VBScript etc). Seems to be a few things posted on Technet Gallery (to be gone soon) that will provide the missing piecies. 

Where are we looking? Right here;

image

Use the function Add-RegKeyMember, loop through all profiles and then filter any potential things you want to leave behind – and we should be able to clear out not so active profiles. A few dangerous lines commented out so you can copy and paste at will. 

function Add-RegKeyMember {
 <#
 .SYNOPSIS
Adds note properties containing the last modified time and class name of a 
registry key.

.DESCRIPTION
 The Add-RegKeyMember function uses the unmanged RegQueryInfoKey Win32 function
 to get a key's last modified time and class name. It can take a RegistryKey 
object (which Get-Item and Get-ChildItem output) or a path to a registry key.

.EXAMPLE
 PS> Get-Item HKLM:\SOFTWARE | Add-RegKeyMember | Select Name, LastWriteTime

Show the name and last write time of HKLM:\SOFTWARE

.EXAMPLE
 PS> Add-RegKeyMember HKLM:\SOFTWARE | Select Name, LastWriteTime

Show the name and last write time of HKLM:\SOFTWARE

.EXAMPLE
 PS> Get-ChildItem HKLM:\SOFTWARE | Add-RegKeyMember | Select Name, LastWriteTime

Show the name and last write time of HKLM:\SOFTWARE's child keys

.EXAMPLE
 PS> Get-ChildItem HKLM:\SYSTEM\CurrentControlSet\Control\Lsa | Add-RegKeyMember | where classname | select name, classname

Show the name and class name of child keys under Lsa that have a class name defined.

.EXAMPLE
 PS> Get-ChildItem HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall | Add-RegKeyMember | where lastwritetime -gt (Get-Date).AddDays(-30) | 
 >> select PSChildName, @{ N="DisplayName"; E={gp $_.PSPath | select -exp DisplayName }}, @{ N="Version"; E={gp $_.PSPath | select -exp DisplayVersion }}, lastwritetime |
 >> sort lastwritetime

Show applications that have had their registry key updated in the last 30 days (sorted by the last time the key was updated).
 NOTE: On a 64-bit machine, you will get different results depending on whether or not the command was executed from a 32-bit
       or 64-bit PowerShell prompt.

#>

    [CmdletBinding()]
     param(
         [Parameter(Mandatory, ParameterSetName="ByKey", Position=0, ValueFromPipeline)]
         # Registry key object returned from Get-ChildItem or Get-Item
         [Microsoft.Win32.RegistryKey] $RegistryKey,
         [Parameter(Mandatory, ParameterSetName="ByPath", Position=0)]
         # Path to a registry key
         [string] $Path
     )

    begin {
         # Define the namespace (string array creates nested namespace):
         $Namespace = "CustomNamespace", "SubNamespace"

        # Make sure type is loaded (this will only get loaded on first run):
         Add-Type @"
             using System; 
             using System.Text;
             using System.Runtime.InteropServices; 

            $($Namespace | ForEach-Object {
                 "namespace $_ {"
             })

                public class advapi32 {
                     [DllImport("advapi32.dll", CharSet = CharSet.Auto)]
                     public static extern Int32 RegQueryInfoKey(
                         Microsoft.Win32.SafeHandles.SafeRegistryHandle hKey,
                         StringBuilder lpClass,
                         [In, Out] ref UInt32 lpcbClass,
                         UInt32 lpReserved,
                         out UInt32 lpcSubKeys,
                         out UInt32 lpcbMaxSubKeyLen,
                         out UInt32 lpcbMaxClassLen,
                         out UInt32 lpcValues,
                         out UInt32 lpcbMaxValueNameLen,
                         out UInt32 lpcbMaxValueLen,
                         out UInt32 lpcbSecurityDescriptor,
                         out Int64 lpftLastWriteTime
                     );
                 }
             $($Namespace | ForEach-Object { "}" })
 "@
     
         # Get a shortcut to the type:    
         $RegTools = ("{0}.advapi32" -f ($Namespace -join ".")) -as [type]
     }

    process {
         switch ($PSCmdlet.ParameterSetName) {
             "ByKey" {
                 # Already have the key, no more work to be done :)
             }

            "ByPath" {
                 # We need a RegistryKey object (Get-Item should return that)
                 $Item = Get-Item -Path $Path -ErrorAction Stop

                # Make sure this is of type [Microsoft.Win32.RegistryKey]
                 if ($Item -isnot [Microsoft.Win32.RegistryKey]) {
                     throw "'$Path' is not a path to a registry key!"
                 }
                 $RegistryKey = $Item
             }
         }

        # Initialize variables that will be populated:
         $ClassLength = 255 # Buffer size (class name is rarely used, and when it is, I've never seen 
                             # it more than 8 characters. Buffer can be increased here, though. 
         $ClassName = New-Object System.Text.StringBuilder $ClassLength  # Will hold the class name
         $LastWriteTime = $null
             
         switch ($RegTools::RegQueryInfoKey($RegistryKey.Handle,
                                     $ClassName, 
                                     [ref] $ClassLength, 
                                     $null,  # Reserved
                                     [ref] $null, # SubKeyCount
                                     [ref] $null, # MaxSubKeyNameLength
                                     [ref] $null, # MaxClassLength
                                     [ref] $null, # ValueCount
                                     [ref] $null, # MaxValueNameLength 
                                     [ref] $null, # MaxValueValueLength 
                                     [ref] $null, # SecurityDescriptorSize
                                     [ref] $LastWriteTime
                                     )) {

            0 { # Success
                 $LastWriteTime = [datetime]::FromFileTime($LastWriteTime)

                # Add properties to object and output them to pipeline
                 $RegistryKey | Add-Member -NotePropertyMembers @{
                     LastWriteTime = $LastWriteTime
                     ClassName = $ClassName.ToString()
                 } -PassThru -Force
             }

            122  { # ERROR_INSUFFICIENT_BUFFER (0x7a)
                 throw "Class name buffer too small"
                 # function could be recalled with a larger buffer, but for
                 # now, just exit
             }

            default {
                 throw "Unknown error encountered (error code $_)"
             }
         }
     }
 }


             $profiles = Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" | Add-RegKeyMember | Select Name, LastWriteTime
             foreach ($p in $profiles) {
             
             if ($p.lastwritetime -lt $(Get-Date).Date.AddDays(-90)) {
                     
                     $key = $($p.name) -replace "HKEY_LOCAL_MACHINE","HKLM:"
                     $path = (Get-ItemProperty  -Path $key -Name ProfileImagePath).ProfileImagePath
                     $path.tolower()
                     if ($path.ToLower() -notlike 'c:\windows\*' -and $path.ToLower() -notlike 'c:\users\adm*') {
                         write-host "delete " $path
                         #Get-CimInstance -Class Win32_UserProfile | Where-Object { $path.split('\')[-1] -eq $User } | Remove-CimInstance #-ErrorAction SilentlyContinue
                         #Add-Content c:\windows\temp\DeleteProfiles.log -Value "$User was deleted from this computer."
                     }

                }
             }

WSUS Cleanup

Despite multiple articles of howto maintain a WSUS database for performance and scalability – there was always a performance issue dragging across which made all the cleanup jobs take forever. An earlier (not able to find it again) Technet Forum post clarified that this was due to all clean-up jobs beeing dependent on the Stored Procedure spDeleteUpdate, and this generates a temporary table that doesnt have an index for the appropiate columns. Unfortunately I werent able to find this post again – but lo, and behold – Microsoft confirmed the behaviour!

The fix is simply to alter the Stored Procedure spDeleteUpdate and append the following line during the creation of the temporary table;

image

EnableFastFirstSignin – howto set it up

EnableFastFirstSignin seems to be a semi-announced feature that is only possible to configure using a Provisioning Package. I think the official documentation states that it is in preview.

But, let’s not care about that

What is it?

This:

Is there  any documentation for this seemingly awesome black magic?

Yes, at Docs@Microsoft

How do I set it up?

Prerequisites

Windows 10 – 1809

Windows ADK – 1809 – Windows Imaging and Design Configuration

Brief overview of what you need todo;

· Configure Provisioning Package

· Generate Provisioning Package

· Install Provisioning Package

Configure / Generate package

Start Windows Imaging and Configuration Designer
Press to create Advanced Provisioning
Define the following settings:
Runtime Settings -> Policies -> Authentication ->

EnableFastFirstSignIn

Select Enabled

Runtime Settings -> SharedPC -> EnabledSharedPCMode

Select TRUE

Runtime Settings -> SharedPC -> AccountManagement -> AccountModel

Select Domain-joined only

Select Export -> Provisioning Package
Enter information regarding name of package, version. Information is arbitrarily set.

Owner is: IT Admin

Rank: 0 or 1

Press Next

Press Next
Select where to save the Provisioing Package

Press Next

Press Build
Press Finish

Install Package

Ensure you are using Windows 10 – 1809
Open an elevated Powershell prompt, using a local administrative account
NOTE: PackagePath is unique to the package name and environment you are working

Execute the following command to install:

Install-ProvisioningPackage -PackagePath “sample-name.ppkg” -QuietInstall

After this the awesome experience should be on whatever endpoint you installed this on. As far as I can tell all that remains is Group Policy Object processing.

What happens in the background?

What does this magical black box of awesomeness actually do in the background? Microsoft has little to reveal, however quite a few people have posted findings on Twitter so far

Trenteye seem to be digging into this further and this is what has been shared so far;

Lenovo and BIOS Settings

Lenovo has provided numerous series of devices, which apparently do not have an aligned standard of handling BIOS / UEFI settings or BIOS / UEFI upgrades. As part of this headache and to ease the burden of jumping between the different device types I started to dive in to harmonize our scripts. As previously written Lenovo has a few odd design decisions – you can for example not reduce security via script. To disable TPM or SecureBoot you would be required to alter this via a keyboard. However, setting the initial password (increasing the security) also requires that you do this by manually typing it into the settings. In addition to the above challenges I reached the following conclusion;

1. It is most likely only relevant to enable any setting.
2. Handling passwords stinks (setting them etc)
3.  Settings between different models in a series can vary by name and vary by name of the possible values to set
4. WMI-classes used differs between series (ThinkStation, ThinkPad and ThinkCentre)

For example, ThinkPad has the GetBIOSSelections class, which can present the different possibilities for a specific setting. However, ThinkCentre does not have this class, but instead present options via a [bracket] indicator in the output of the currentsetting.

How would one deal with all this mess?

Two functions are provided – one for figuring our howto enable a specific setting. The other one for setting each setting. Its ugly – as the more models, bios-versions and whatnot I get a hold of the uglier variations of this I find. The initial attempt was to keep it as generic as possible.The best way to explain what I have todo sometimes;

 

Function Get-LenovoBIOSSettingValue {
    param(
        [Alias("BIOSSetting")]
        [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Array]$Setting
        
    ) 

    begin {
            $complex = $null
            if(Get-WmiObject -namespace root\wmi -List | where { $_.Name -eq "Lenovo_GetBiosSelections"})
            {
                      $complex = $false
                      Write-verbose "Lenovo GetBiosSelections does exist"
            }
            else
            {
                      $complex = $true
                      Write-verbose "Lenovo GetBiosSelections does not exist"
            }
    }

    process {
            foreach ($s in $setting) {
                $value = $null
                $selections = $null
                $findsetting = $null
        
                If ($complex -eq $true) {
                    $findsetting = gwmi -class Lenovo_BiosSetting -namespace root\wmi | Where-Object {$_.CurrentSetting.split(",",[StringSplitOptions]::RemoveEmptyEntries) -like $s} | Select-Object CurrentSetting
                    try {
                        $value = $findsetting.CurrentSetting.split(",",[StringSplitOptions]::RemoveEmptyEntries)[0]
                    }
                    catch {
                       # write-output "Unable to convert $s to value"
                    }                        
                    $r = [regex] '(?<=\[).+?(?=\])'
                    try {

                         [string]$checkvalue = $findsetting.CurrentSetting.split(",",[StringSplitOptions]::RemoveEmptyEntries)[1].split(";",[StringSplitOptions]::RemoveEmptyEntries)[0]
                         $appliedselection = $checkvalue
                        if ([string]$($findsetting.CurrentSetting.split(";",[StringSplitOptions]::RemoveEmptyEntries)[1]) -Match 'Status:ShowOnly') {


                            if($checkvalue -like  "Active*" -or $checkvalue -like  "Enabl*"  -or $checkvalue -like 'Discrete*TPM*' ) 
                            {
                                    if ($value -like 'Security*Chip*') {
                                            $value = $null
                                            $selections = $null
                                            $findsetting = $null
                                    }
                                
                            }
                            else {
                                $value = $null
                                $selections = $null
                                $findsetting = $null
                            }
                        }
                    }
                    catch {
                        #noerror
                    }
                    $selections = ($r.match($findsetting.currentsetting).value).split(":").split(",") | Where-Object {$_ -like "Active*" -or $_ -like "Enabl*" -or $_ -like 'Discrete*TPM*' }
                    
                    if ($selections -is [array] -and $selections -contains 'Enable') {
                        $selections = $selections| Where-Object { $_ -like "Enabl*" }
                    }
    
                }
                else {

                    
                    try {
                        $findsetting = Get-WmiObject -Class Lenovo_BiosSetting -Namespace root\WMI | Where-Object {$_.CurrentSetting -match $s} | Select-Object CurrentSetting
                        $value = $findsetting.currentsetting.split(",")[0]
                        $appliedselection = $findsetting.currentsetting.split(",")[1]
                    }
                    catch {
                        #write-output "Unable to convert $s to value"
                    }

                    $selections = ((gwmi –class Lenovo_GetBiosSelections –namespace root\wmi).GetBiosSelections($value).selections).split(",") | Where-Object {$_ -like "Active*" -or $_ -like "Enabl*" }
                    if ($selections -is [array] -and $selections -contains 'Enable') {
                        $selections = $selections| Where-Object { $_ -like "Enabl*" }
                    }
                }
                if ($value -ne $null -and $selections -ne $null) {
                    $object = New-Object –TypeName PSObject
                    $object | Add-Member –MemberType NoteProperty –Name Setting –Value $($value)
                    $object | Add-Member –MemberType NoteProperty –Name Current –Value $($appliedselection)
                    $object | Add-Member –MemberType NoteProperty –Name Selection –Value $($selections) 
                    $object

                    #write-output "$s provided $value, which can be set to $selections"
                }
            }
    }
}

Function Set-LenovoBIOSSetting {
    param(
        [Alias("BIOSSetting")]
        [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Array]$Setting,
        [Parameter(Position = 1, Mandatory = $false)]
        [String]$Password
        
    )

    begin {
        $wmi = Get-WmiObject -Class Lenovo_SetBiosSetting -Namespace root\wmi -ErrorAction Stop
        $object = $null
        if ($object) {
            Remove-Variable -Name $object
        }
        #$setting
    }

    process {
    
     
        foreach ($s in $setting) {
        #$s
            $result = $null
                 if ($s.Current -ne $s.Selection) {
                       $result = ($wmi.SetBiosSetting("$($s.setting),$($s.selection)$password")).return
                        $object = New-Object –TypeName PSObject
                        $object | Add-Member –MemberType NoteProperty –Name Setting –Value $($s.setting)
                        $object | Add-Member –MemberType NoteProperty –Name Current –Value $($s.selection)
                        $object | Add-Member –MemberType NoteProperty –Name Result –Value $result
                        $object
                 }
                 else {

                        $object = New-Object –TypeName PSObject
                        $object | Add-Member –MemberType NoteProperty –Name Setting –Value $($s.setting)
                        $object | Add-Member –MemberType NoteProperty –Name Current –Value $($s.Current)
                        $object | Add-Member –MemberType NoteProperty –Name Result –Value 'Not updated'
                        $object
                }
         }
    }

}

Supersedence and how it is not a feature

Image result for configuration manager logoSupersedence was supposedly a great new feature for ConfigMgr 2012 (released in… you guessed it..) that was part of the Application Model. App-model held so many promises, however it was actually quite broken until a few service packs and fixes down the road. There are still quite a few defects that just are “known” by people and adds to the general feeling of ConfigMgr beeing complex.

After a twitter-rant on how Microsoft view their support organisation (and anyone paying / using it) as a third-class citizien with no rights – apart from beeing the therapist for whatever sysadmin is on the other end of the call – I became sort of frustrated that the sixth most top voted Uservoice item, something that I previously raised support calls within their organisation aswell as spent better part of a week (at Microsoft Seattle campus) ranting about this specific defect to just about anyone who potentially could reach the actual Product-Caretaker to fix the issue. Microsoft may spring out shiny new things – but it honestly sucks at fixing its products. If its broken – it’s still just plain broken. And even when its fixed – noone tells you its fixed apart from during watercooler talk.

How long has supersedence been broken? Since 2012

Update 2017-12-18:
ConfigMgr TechPreview 1712 was released with a specific fix that would potentially aim to resolve test 1 and 2. If you are on a support agreement it seems that potentially you could drop it and only relay requests via UserVoice.

What is the scenario?

So, a few things to get the setup of what the actual defect is. Supersedence is the intent to connect two Applications within ConfigMgr and inform the system how it should replace the older one/ones. For the defect to show itself the following has to be inplace;

  • Application has to be deployed (directly / indirectly) as Available
  • Application can be targeted to users / computers
  • Application made Available has to replace a previous old version that is installed on the given impacted client

FoxDeploy (Stephen Owen) wrote a great article that sets up the premise for the defect, however as far as I understand his target environment deals only with users (or user collection targeted deployments).  Apart from really sharing a few thoughts, creating a very popular UserVoice-item there are somethings that needs to be retested (previous extensive confirmation of this defect was in the ConfigMgr 2012 era – both for myself and FoxDeploy) with the newer versions of ConfigMgr.

There is no release statement from Microsoft that this has been altered, fixed or improved upon as far as the eye can see.

Based on the above we can also conclude that this is not relevant if;

  • All deployments are Required
  • Supersedence is not used
  • Older versions, now beeing replaced, are not installed on any endpoints

Tests

We will retry the following tests on a ConfigMgr 1702 site. Neither 1706 and 1710 has introduced anything remotely closing to fixing this behaviour and all rumours that people have heard about previous fixes have been pre-CurrentBranch.

Applications used while testing

image

Test 1

  • Application (Adobe Reader DC 18) will supersed an older version (Adobe Reader DC 17)  before creating the deployment
  • A deployment will be created that targets our specific computer that has Adobe Reader DC 17 installed
  • Deployment will target a computer collection
  • Deployment will be set to be made available

Result

When creating the deployment the following new check-boxes will become painfully obvious for the administrator. Pay attention, and ConfigMgr will tell you exactly how you are about to wreak havoc within the environment. Despite that you are saying – please just offer this as a nice-to-have-thingy – there is a greyed out check-box that indicates that a required deployment is technically beeing created.

image

When setting the date/time the deployment will be available – this becomes even more painfully obvious that you are infact creating a required deployment

image

As you might have guessed – as soon as the client receives this new policy – Adobe Acrobat Reader DC 17 is upgraded to Adobe Acrobat Reader DC 18.

Consistent with ConfigMgr 2012 in 2012

Test 2

  • Application (Adobe Reader DC 18) will supersed an older version (Adobe
    Reader DC 17)  after creating the deployment
  • A deployment will be created that targets our specific computer that
    has Adobe Reader DC 17 installed
  • Deployment will target a computer collection
  • Deployment will be set to be made available

Result

As you can imagine the wizard while creating the deployment as Available does not present any information regarding supersedence (as that relationship between the two applications does not exist yet)

image

After creating the supersedence relationship and then opening the properties for the specific Available deployment the following reveals itself. Same as when Foxdeploy concluded the testing early 2016.

image

The scheduling leaves no schedule set for when supersedence should be run.

image

For a brief moment on the client the two versions will reveal themselves

image

Unlike Foxdeploys testing (that were targeted for user collections) – the upgrade will still occur and now its running on the schedule that you can not control. At the latest of the next Application Evaluation cycle – the upgrade will take place.

Consistent with ConfigMgr 2012 in 2012

Test 3

  • Application (Adobe Reader DC 18) will supersed an older version (Adobe
    Reader DC 17) before deployment is created
  • The application themselves will not be deployed, but a Task Sequence containing one step installing Adobe Reader DC 18 will be made available.
  • A deployment (for the task sequence) will be created that targets our specific computer that
    has Adobe Reader DC 17 installed
  • Deployment will target a computer collection
  • Deployment will be set to be made available

Contents of Task Sequence

image

Result

As the task sequence only contains the step to install the one specific application there is no possibility to select that this would be available for PXE-only or other options than the ConfigMgr-client. The Task Sequence is still set to only be available. Per my experience it is also recommended to set the Available time back 1h (or 1 day) to get fairly instant results, otherwise you need to wait a bit before the client start processing the information.

image

As per the hallway chatter this seems to be resolved and the Adobe Acrobat Reader DC 18 does not cause an automatic upgrade.

Not consistent with ConfigMgr 2012 in 2012

Workaround

What have we learnt? Well – anything that depends on supersedence will have a required deployment instead of an available deployment. There are of course three options to deal with this and they are as follows

  • Script it. Powershell Application Deployment Toolkit offers great ways todo this with control.
  • Set up the supersedence relationship, and then create the available deployment and alter the time for the supersedence from ASAP to a date far far into the future. Perhaps so far that you will not be handling the site any more.
  • Follow FoxDeploys guidance for user deployments and create the deployment, and then the supersedence relationship.

 

 

 

Apple iTunes 12.7 and Software License Agreement registry key

Image result for itunesiTunes is the ugly step-child (or the Meg, or the bastard) of the Apple family. As iPhone still remains the phone of choice within my family and quite a few corporations there is still a need from time to time to deal with this excuse of a software.

As a revisit to the previous post where I did track down howto eliminate the end-user requirement to accept the Software License Agreement on corporate installations – there has been some changes in the years past. As the software has decided the progress updates are evil and show no indication of pretty much anything moving this will be how time is spent to track the latest version.

Previously to identify the necessary parts to avoid presenting the SLA for the end-user we required two parts. The first is an identifier for the SLA, and the second was where to put this identifier in the registry.

Registry

Second things first. The registry key still remains the same. The below is the registry key necessary for a Windows 10 x64 installation of Apple iTunes 12.7.

Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Apple Computer, Inc.\iTunes]
"SLA"="EA1511"

 

SLA value

To retrieve the value (EA1511) we need a slightly different process than previously used. The value was stored in iTunesPrefs.xml in the earlier versions of iTunes, however that file doesn’t exist anymore and instead we can see the following files

image

Opening all of them will reveal binary or hexadecimal-files and neither will allow us to decipher anything we need. As the SLA is most likely located within the installation folder we can poke around and see if the actual SLA will provide us with anything.

Locating the RTF-file License within en.lproj (for english users) could potentially contain something useful.

image

Opening the document and scrolling around it will reveal the two last lines at the bottom of the document

image

And there it is!

End result

image

ConfigMgr – Howto generate an inbox-backlog

Ever wondered how someone can generate an enormous inbox-backlog for a Configuration Manager site? Apart from not scaling the environment correctly based on the performance of the infrastructure and the number of clients and features its servicing – here are a two methods to bring pretty much everything to a halt.

Server Groups

Server Groups is a feature not yet released for production. It became available in ConfigMgr 1606 and has been worked on since. As one can check it is required to be enabled from Updates and Servicing under Administration in the Feature list, however its been toggled ‘On’ in a number of sites where noone has actively thought about using this feature. Perhaps its toggled on per default?

image

There are a great number of write-ups by independt specialists aswell as an extensive Microsoft documentation.

The Server Group is defined per collection, and there are a few safeguards in place to avoid harm – for example you can’t se this on the generic ConfigMgr collections – such as the All systems

image

Any other collection, regardless of how many members it has, can toggle this on.

image

Locating these collections that are enabled for Server Groups is fairly easy by simplying recursively searching for all collections that has Server Groups to Yes – check the Server Group search criteria.

image

image

Create the backlog

The intent is to control Software Updates and in what order they are applied. If Server Group is enabled for a collection with all (or many devices) and these devices have deadline for a Software Update set – things will happen directly after these deadlines. Each client will effectively send a status update regarding patching status roughly once every 5 minutes (or each minute?)

Now, watch that backlog go…

Relationships – Dependency or Supersedence

Deeply burried with a side-note on howto create Dependencies within a Configuration Manager application is the following limit specified;

In some cases, a deployment type is dependent on a deployment type that also contains dependencies. In this scenario, where a chain of dependencies exists, the maximum number of supported dependencies in the chain is five.
Source: Technet¨

After effective testing the following is concluded – this isn’t a hard-limit that measures the limit of 5 level deep dependency chain – but rather a complete halt of so many things when its to  ‘complex’ and ‘to large’. Or – its not the exact number of 5 that is the limit but rather that depending on loads of factors (complexity of application object, how many, how deep etc)  this will eventually generate a failure of the handling within the Configuration Client or simply to many status messages sent. Also – the limit is very applicable for supersedence.

A chain of 5 dependencies or supersedences is a very good estimate for when things will function, and beyond that you are at the mercy of luck and testing to see if things work.

What adds payload and therefore risk of failure?

  • Requirements
  • Detections
  • Conditions
  • Dependencies
  • Many levels of dependency
  • Supersedence
  • Many levels of supersedence

The more you have of the above the more has to be evaluated and processed. As a consultant I have learned the hardway to avoid using the ‘always’, ‘never’ etc. Usually time is saved if dependencies and supersedence is limited in use. Wrap it in whatever preferred scripting language you have.

Now, if you have neatly packaged and deployed all versions of Java or Adobe for the last 2-3 years – there should be quite a few versions available within ConfigMgr. Using supersedence this is a great way to start generating that backlog;

Create the backlog

Create the latest version and ensure you replace about 8 previous versions in the first level. After the first level there should be roughly 7 deeper levels for each one in the first level. Something in the lines of

image

Aim to deploy the application to any collection that contains many devices and watch that backlog start creeping up…

Why create this havoc?

The intent is not to teach novice users howto wreak havoc within an environment, but rather what potentially could cause a backlog in the ConfigMgr processing and howto identify ill advised practices within a company.