master-client(-management)

Take Windows Up to 11

Page 2 of 2

Group Policy Security Baselines and Windows as a Service – a Layered Approach

How to align the rollout of the Microsoft Security Baselines Group Policies with the Windows 10 servicing model

Update: Added WMI-Filter for Windows 10 20H2

The Problem

Microsoft released security baselines in form of a Group Policy backup set for its operating systems in the recent years. Many enterprises are using these baselines as a security foundation. Enterprises have to adopt new settings on a lot higher frequency with the change of the servicing model and the additional release speed of Windows 10. New security baselines are now available with every release of Windows 10 every 6 months.

Note: If you want to learn more about Windows as a Service look here

The nature of Group Policies where small changes can have a huge impact on your client landscape made it necessary for enterprises to build solid change processes around them to document and verify any change. These processes are normally slow and inflexible which makes it very hard to combine them with the fast speed of new security baselines.

Another challenge for enterprises is the complexity of testing each baseline setting against a variety of several hundred applications. The traditional way was to do this in an OS upgrade project.
First, the complete baseline was activated and then redefined them during application testing. But with Windows 10 branch upgrades there are no upgrade projects and to validate a baseline with over 50 changed settings against your client landscape on a regular basis is not a feasible scenario for many companies.

Solution

In order to help the security settings keeping track with the speed of the baseline releases I am using a layered approach.

What does “layered” mean?

I distinguish between two sorts of Group Policies, the Baseline-GPOs and Custom-GPOs. The main difference between these two are that Baseline-GPOs are not changed by me at all. Every setting which differs from the baselines is made in a Custom-GPO.

Another difference between the Baseline-GPOs and the Custom-GPOs is that the baselines are filtered via WMI-Filter to the corresponding Build version of Windows 10. In contrast the Custom-GPOs are filtered to apply on all Windows 10 clients.

The WMI-Filters

We need a WMI Filter for Windows 10 and for every active Build currently used. Microsoft supports the last three Build versions so you should have a maximum of three (maybe four) active builds and WMI-Filters.

WMI Filter for Windows 10 1709

Windows 10
Select * from Win32_OperatingSystem WHERE Version LIKE "10.0%" and ProductType = "1"

Windows 10 1607
Select * from Win32_OperatingSystem WHERE Version LIKE "10.0.14393%" and ProductType = "1"

Windows 10 1703
Select * from Win32_OperatingSystem WHERE Version LIKE "10.0.15063%" and ProductType = "1"

Windows 10 1709
Select * from Win32_OperatingSystem WHERE Version LIKE "10.0.16299%" and ProductType = "1"

Windows 10 1803
Select * from Win32_OperatingSystem WHERE Version LIKE "10.0.17134%" and ProductType = "1"

Windows 10 1809
Select * from Win32_OperatingSystem WHERE Version LIKE "10.0.17763%" and ProductType = "1"

Windows 10 1903
Select * from Win32_OperatingSystem WHERE Version LIKE "10.0.18362%" and ProductType = "1"

Windows 10 1909
Select * from Win32_OperatingSystem WHERE Version LIKE "10.0.18363%" and ProductType = "1"

Windows 10 2004
Select Version,ProductType from Win32_OperatingSystem WHERE Version LIKE "10.0.19041%" and ProductType = "1"

Windows 10 20H2
Select Version,ProductType from Win32_OperatingSystem WHERE Version LIKE "10.0.19042%" and ProductType = "1"

The WMI-Filters contain a query about the Windows Version and the ProductType. The latter is defined as follows

  • 1 – Client Computer
  • 2 – Domain Controller
  • 3 – Member Server

With these filters we make sure that the Windows 10 GPOs will only apply on Windows 10 client devices (of the defined Build Version).

The Baseline-GPOs

You can download the Baseline-GPOs from here.

I have written a short PowerShell function to import all baselines at once. You just have to export them in one folder and add ‘-Version’ (e.g. ‘-1709’) to the folder name.

extracted baseline

Then change the ExportPath to your folder path in the following script and execute it. You will need to import the Group Policy WMI filter cmdlet module prior to successfully running the script.

#Requires -Modules GPWMIFilter
#Source: https://gallery.technet.microsoft.com/scriptcenter/Group-Policy-WMI-filter-38a188f3
function Import-GPOBaselines {
param(
# Specifies a path to one or more locations.
[Parameter(Mandatory = $true,
Position = 0,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
HelpMessage = "Path to the folder containing the exported GPO baseline folder.")]
[Alias("PSPath")]
[ValidateNotNullOrEmpty()]
[ValidateScript( {Test-path -Path $_ -PathType Container} )]
[string[]]
$Path
)
$reports = Get-ChildItem -Recurse -path $Path -Filter 'gpreport.xml'
foreach ($report in $reports) {
[XML]$XML = Get-Content -Path $report.fullname
$BuildVersion = $report.FullName.Split('\')[-4].split('-')[-1]
$GPName = $BuildVersion + ' - ' + $Xml.gpo.Name
$BackupID = $report.FullName.Split('\')[-2].replace('{', '').replace('}', '')
$BackupPath = Split-path -path (Split-Path -path $report.FullName -Parent) -Parent
$GPO = Import-GPO -BackupId $BackupID -Path $BackupPath -TargetName $GPName -CreateIfNeeded
$wmifilter = Get-GPWmiFilter -All | Where-Object {$_.name -match $BuildVersion}
$GPO.WmiFilter = $wmifilter
}
}
Import-GPOBaselines -Path 'ExportPath'

The script will create or update the GPOs and name them as you can see in the picture below. Additionally it will set the corresponding WMI-Filter if it includes the Build number (e.g. 1709)

Imported Windows security baselines

Create the Custom-GPOs

You can create a Custom-GPO for each corresponding type of baseline (Defender, Computer, …) or as I did in the example below just one Custom-GPO for all baselines.

Custom GPO

Linking the GPOs

After having everything in place we can now link the GPOs to the OU(s). In the next picture, you can see the GPO Link order of my Windows 10 OU.

Linked Group Policies

The Custom-GPOs have to be linked with a lower order number or to a Sub-OU to apply at last and overwrite the Baseline-GPO if needed.

Example

A common baseline setting which many of my customers perceive as too strict is the UAC configuration in the baseline for Standard Users which is set to Automatically deny elevation requests.

UAC Baseline

In the Custom-GPO I changed that setting to Prompt for credentials on the Secure Desktop

UAC custom setting

As you can see in the screenshot of a Group Policy Result of a Windows 10 1709 client the baselines are applied as described and the UAC setting is overwritten by the Custom-GPO.

Group Policiy Result

What is the advantage?

Instead of integrating and validating every single new baseline setting you only have to import the new Baseline-GPOs and the corresponding WMI-Filter.

Microsoft released the baselines when the Windows 10 Build became available in the Semi-Annual-Channel (formerly known as Current Branch for Business). With the release of the Fall Creators Update the final version of baselines even became available with the release to the Semi-Annual-Channel(targeted) (formerly known as Current Branch). So, it is very unlikely that you have deployed a large number of clients with the newest build before the baselines are available.

Therefore, when you start to upgrade your clients to the newest build you will automatically test the new baselines along with the new OS Version without an effect on your productive clients.

If you have to change a setting in your Custom-GPOs because of the new baselines it is very unlikely that this setting will have a negative effect on your existing clients. Because it is either a new setting which isn’t applicable for the old builds or it isn’t set in the old baselines. If the latter is the case you will set it back to the default value in most cases which already worked.

It also makes it easier to find out which of your settings differ from the baselines. You do not have to compare different GPOs with the baselines. You only have to look at your Custom-GPOs or in a Group Policy Result Report which of the settings are applied from a Custom-GPO.

Create LAPS managed user with SCCM Configuration Item

Microsoft has released LAPS (Local Administrator Password Solution) to easily allow different complex passwords for the local Administrator account on every client. It also allows to manage another user than the Built-in Administrator with the Well-Known SID (-500). But it does not create such a user.

In this article, I show you how to configure a SCCM Configuration Item to create such a user with a dynamic password.

Update: I removed an issue in the remediation script which did not always delete the password expiration time in a multi domain environment.

I won’t go into the details of configuring LAPS in your environment, there are already some really good articles about that topic.

The validation script

The validation script checks the following:

  • is LAPS enabled?
  • is LAPS installed?
  • is an Admin Account Name specified in the GPO?
  • Does the Admin Account exist?

The remediation script

The remediation script creates a local user with the name specified in the Group Policy and sets a random complex password. After that it deletes the expiration time attribute (ms-Mcs-AdmPwdExpirationTime) from the Active Directory computer object so that LAPS will set a new password on the next policy update. Finally, it triggers a policy update.

It does not add the user to the Administrator group. I recommend to do this with Group Policy.

Group Policy setting

If you want to manage another local user than the Built-in Administrator you have to configure the following policy setting in your Group Policies:

Computer Configuration\Policies\Administrative Templates\LAPS\Name of the administrator account to manage

Set it to enabled and enter the name of the local account you want to create.

LAPS GPO setting

Configuration Manager

Create Configuration Item

In the SCCM console go to Assets and Compliance - Compliance Settings - Configuration Items and click on the Create Configuration Item .

Specify a name and select Windows Desktops and Servers (custom) as type.

Select the Operating system versions you want to support (requires PowerShell).

Click on the New… button.

Specify a name for the setting and select as Setting type Script and as Data type String.

Click on the upper Edit Script… button in the Discovery script area. Then select PowerShell, and copy paste the following script to the script area.

function Get-LocalUserAccount {
[CmdletBinding()]
param (
[Parameter(
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true
)]
[string[]] $Computer = $env:COMPUTERNAME,
[Parameter(Mandatory = $true)]
[string] $Name
)
foreach ($item in $Computer) {
[ADSI] $host = [string]::Format("WinNT://{0}", $item)
if ($Name) {
foreach ($user in $Name) {
$host.Children | where { $_.SchemaClassName -eq "User" -and $_.Name -eq $user }
}
}
else {
$host.Children | where {$_.SchemaClassName -eq "User"}
}
}
}
$AdminAccountName = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft Services\AdmPwd' -Name 'AdminAccountName' -ErrorAction SilentlyContinue).AdminAccountName
$item = Get-LocalUserAccount -Name $AdminAccountName
if ($item -eq $null -and $AdminAccountName -ne $null -and ((Get-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft Services\AdmPwd' -Name 'AdmPwdEnabled' -ErrorAction SilentlyContinue).AdmPwdEnabled) -eq '1' -and (Get-Item -Path ($env:ProgramFiles + '\LAPS\CSE\AdmPwd.dll') -ErrorAction SilentlyContinue)) {
return $false
}
else {
return $true
}
view raw Detection.ps1 hosted with ❤ by GitHub

Do the same with the lower Edit Script… button in the Remediation Script area with the following script.

function New-LocalUserAccount {
[CmdletBinding()]
param (
[Parameter(
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true
)]
[string] $Computer = $env:COMPUTERNAME,
[Parameter(Mandatory = $true)]
[string] $Name,
[Parameter(Mandatory = $true)]
[string] $DisplayName,
[Parameter(Mandatory = $true)]
[string] $Password
)
[ADSI] $host = [string]::Format("WinNT://{0}", $Computer)
if (![string]::IsNullOrEmpty($Name)) {
$user = $host.Create("User", $Name)
if ($user -ne $null) {
$user.SetPassword($password);
$user.SetInfo()
}
}
}
function New-RandomPassword {
[CmdletBinding()]
param (
[Parameter(Mandatory = $false)]
[int] $Length = 12,
[Parameter(Mandatory = $false)]
[string] $RegEx = '[\w\$\%\&\/\(\)\=\?\!\\,\.\-_\:;\]\+\*\~<>\|]'
)
[string] $password = -join ( [char[]](0..127) -match $RegEx | Get-Random -Count $length )
return $password
}
function Remove-AdmPwdExpirationTime {
$Searcher = New-Object DirectoryServices.DirectorySearcher
$Searcher.Filter = '(&(sAMAccountName=' + $env:ComputerName + '$))'
$computerObject = New-Object DirectoryServices.DirectoryEntry ($Searcher.FindAll()).Path
if ($computerObject.servicePrincipalName -match ($env:ComputerName + '.' + (Get-ItemProperty -path 'HKLM:\\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters' -Name 'Domain').Domain)) {
if ($computerObject.'ms-Mcs-AdmPwdExpirationTime') {
$computerObject.'ms-Mcs-AdmPwdExpirationTime'.Remove($($computerObject.'ms-Mcs-AdmPwdExpirationTime'))
$computerObject.setInfo()
}
}
}
$AdminAccountName = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft Services\AdmPwd' -Name 'AdminAccountName' -ErrorAction SilentlyContinue).AdminAccountName
If (($AdminAccountName) -and ((Get-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft Services\AdmPwd' -Name 'AdmPwdEnabled' -ErrorAction SilentlyContinue).AdmPwdEnabled) -eq '1' -and (Get-Item -Path ($env:ProgramFiles + '\LAPS\CSE\AdmPwd.dll') -ErrorAction SilentlyContinue)) {
New-LocalUserAccount -Name $AdminAccountName -DisplayName $AdminAccountName -Password (New-RandomPassword -Length 24)
Remove-AdmPwdExpirationTime
Start-Process -FilePath ($env:windir + '\system32\gpupdate.exe') -ArgumentList '/force'
}
view raw Remediation.ps1 hosted with ❤ by GitHub

Change to the Compliance Rules Tab and click on the New… button.

Define a Name for the rule select Rule type Value. The value returned by the specified script should be Equals the following values True.

Make sure you select the Run the specified remediation script when this setting is noncompliant checkbox.

You can choose the severity of this rule. For me Warning is high enough.

After that you can complete the creation of the Configuration Item.

Create Configuration Baseline

Now you have to create a Configuration Baseline in Assets and Compliance - Compliance Settings - Configuration Baselines .

Choose a Name for the baseline and Add the configuration item you have created earlier.

Deploy Configuration Baseline

After that you can Deploy the Configuration Baseline to a collection.

Please make sure to select the Remediate noncompliant rules when supported and the Allow remediation outside maintenance window check boxes.

Besides,you have to select how often this rule will be checked. I selected once per day.

Test the Configuration Baseline

After successfully deploying the baseline you should check the Configurations Tab in the Configuration Manager Properties Control Panel on one of your clients.

If the rule was not already evaluated press the Evaluate button.

After successfully evaluating the rule it will be shown as Compliant and the user was created.

The LAPS agent now has a target user and will soon change the password of the user and save this new password to the Active Directory object of the computer.

Hints

  • Check the DcmWmiProvider.log if you get any errors executing the baseline. There you can see the real PowerShell error.
  • If you see a message there like the one in the screenshot below you have to configure PowerShell execution policy to Bypass in the Computer Agent section in the Client settings or you have to sign the scripts with a Code-Signing-Certificate.


SCCM CB client push is not working on devices with TP agent

Problem

If you have installed the SCCM agent of a recent Technical Preview Build on a client it is not possible to push the current branch agent to it. I tested this with TP 1706 / CB 1702 and with TP 1707 / CB 1706.

This does not work even if the options Always install the client and Uninstall existing Configuration Manager client before the client is installed are selected.

As you can see in the log the request is skipped because a newer agent version is already installed.

Solution

You have to manually uninstall the Technical Preview agent before pushing from the CB console

%windir%\ccmsetup\ccmsetup.exe /uninstall

Task sequences are not showing up in SCCM Software Center when multiple users are logged on

Problem

I recently ran into the problem that the task sequence I wanted to test won’t show up in the SCCM Software Center. I checked for a common misconfiguration like

  • Deployment schedule
  • Configuration Manager Client active
  • Client in the correct collection
  • Deployment deployed to the correct collection
  • Client in a boundary with a distribution point
  • Packages deployed to the Distribution Point
  • Check the _SCCLient_%USER%.log, LocationServices.log, PolicyAgent.log, PolicyEvaluator.log,…
  • etc.

But all this was configured correct or did not show any errors.

Solution

I found out after some time that my colleague was still logged on to this computer. After logging him off the Deployments appeared as expected in the Software Center.

Knowing what to look for I found this thread:

Technet: Applications but not Programs showing in Software Centre

What have I learned:
Check if you have the lowest session ID when multiple sessions exist!

Newer posts »