Windows Server 2016 introduced the “AppContainer”, which is created when users connect to the server for the first time. Due to a registry leak, the Windows Server generates registry bloat for firewall rules.
This registry bloat can be found in the following registry keys:
HKLM\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\FirewallRules
HKLM\System\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\RestrictedServices\Configurable\System
Over time, this registry bloat can cause the following symptoms:
Server hang
This behavior is not limited to RDS, however; RDS scenarios where User Profile Disks are used may have a greater impact given that profiles are removed by default from User Profile Disks when a user disconnects.
In a few months, RDS servers can reach more than 200,000 bloated registry items!
The most important task is to monitor your Windows 2016 RDS servers!
A VBS script executed via the Nagios agent by the NetEye server can help monitor the problem before it can destabilize the RDS server:
””””””””””””””””””””””””””””””””””””””””””””””””
‘
‘ NAME: check_reg_count_values.vbs
‘ VERSION: 1.0
‘ AUTHOR: Alessandro Romboli (alessandro.romboli@wuerth-phoenix.com)
‘
‘ COMMENT: Script for counting values in a registry key
‘ for use with Nagios and NSClient++
‘
‘ Modification History: 14-11-2018 Creation
‘
‘
””””””””””””””””””””””””””””””””””””””””””””””””
‘ NAGIOS and NSClient++ CONFIGURATION:
‘
‘ ### command definition ###
‘
‘ define command {
‘ command_name check_mscs
‘ command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -p 5666 -c $ARG1$”
‘ }
‘
‘
‘ ### service definition ###
‘
‘ define service{
‘ use generic-service
‘ host_name MyServer
‘ service_description Registry_Logons_Enabled
‘ process_perf_data 0
‘ check_command check_nrpe!check_reg_count_values -a “HKEY_LOCAL_MACHINE” “SYSTEM\\CurrentControlSet\\Services\\SharedAccess\\Parameters\\FirewallPolicy\\RestrictedServices\\Configurable\\System” “2000” “3000”
‘ }
‘
‘
‘ ### command line ###
‘
‘ ./check_nrpe -H $HOSTADDRESS$ -p 5666 -c check_reg_count_values -a “HKEY_LOCAL_MACHINE” “SYSTEM\\CurrentControlSet\\Services\\SharedAccess\\Parameters\\FirewallPolicy\\RestrictedServices\\Configurable\\System” 2000 3000
‘
‘
‘ ### NSC.ini configuration ###
‘
‘ CheckExternalScripts.dll
‘
‘ [External Script]
‘ allow_arguments=1
‘ allow_nasty_meta_chars=1
‘
‘ [External Scripts]
‘ check_reg_count_values=cscript.exe //T:30 //Nologo scripts\check_reg_count_values.vbs $ARG1$ $ARG2$ $ARG3$ $ARG4$
‘
”””””””””””””””””””””””””””””””””””””””””””””””””””””””’
Option explicit
Dim strHive, strKeyPath, strValueWarning, strValueError
Dim strValue, strHiveKey
dim reg
dim valueNames, types
‘RC to NAGIOS
Const intOK = 0
Const intWarning = 1
Const intCritical = 2
Const intUnknown = 3
‘ Check for arguments
‘WScript.Echo WScript.Arguments.Count
If WScript.Arguments.Count = 0 then
Display_Usage()
ElseIf WScript.Arguments.Count < 4 Then WScript.Echo “not enough arguments supplied.” Display_Usage() ElseIf WScript.Arguments.Count > 4 Then
WScript.Echo “too many arguments supplied.”
Display_Usage()
End if
strHive = UCase(WScript.Arguments.Item(0))
‘ WScript.Echo “strHive = ” & strHive
strKeyPath = WScript.Arguments.Item(1)
‘ WScript.Echo “strKeyPath = ” & strKeyPath
strValueWarning = clng(WScript.Arguments.Item(2))
‘ WScript.Echo “strValueWarning = ” & strValueWarning
strValueError = clng(WScript.Arguments.Item(3))
‘ WScript.Echo “strValueError = ” & strValueError
strHiveKey = strHive
Select Case strHive
Case “HKEY_LOCAL_MACHINE”
strHive = &H80000002
Case “HKEY_CURRENT_USER”
strHive = &H80000001
Case “HKEY_USERS”
strHive = &H80000003
Case Else
WScript.Echo “Unrecognised Registry Hive Key – ” & strHive
WScript.Quit(intUnknown)
End Select
‘WScript.Echo strHiveKey,strKeyPath,strValueWarning,strValueError
set reg = getObject( “Winmgmts:root\default:StdRegProv” )
if reg.enumValues( strHive, strKeyPath, valueNames, types ) = 0 then
strValue= clng(UBound( valueNames ))
end if
If IsNull(strValue) Then
WScript.Echo “Registry Values not found”
WScript.Quit(intWarning)
End If
If (strValue < strValueWarning) Then
WScript.Echo “Value in range: ” & strValue
WScript.Quit(intOK)
ElseIf (strValue < strValueError) Then
WScript.Echo “Warning value: ” & strValue
WScript.Quit(intWarning)
Else
WScript.Echo “Error value: ” & strValue
WScript.Quit(intCritical)
End If
Function Display_Usage
Wscript.StdOut.WriteLine ” check_reg_count_values V. 1.0 by Alessandro Romboli”
Wscript.StdOut.WriteLine “—————————————————————-”
Wscript.StdOut.WriteLine “Usage: cscript.exe check_reg_count_values.vbs ‘REGISTRY_HIVE’ ‘KEY_PATH’ ‘VALUE_Warning’ ‘VALUE_Error'” & vbcrlf
Wscript.StdOut.WriteLine “Examples: ”
Wscript.StdOut.WriteLine “cscript.exe check_reg_count_values.vbs HKEY_LOCAL_MACHINE SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\RestrictedServices\Configurable\System 2000 3000″ & vbcrlf
Wscript.StdOut.WriteLine ” REGISTRY_HIVE = HKEY_LOCAL_MACHINE”
Wscript.StdOut.WriteLine ” KEY_PATH = SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\RestrictedServices\Configurable\System”
Wscript.StdOut.WriteLine ” VALUE_Warning = 2000″
Wscript.StdOut.WriteLine ” VALUE_Error = 3000″
WScript.Quit(intOK)
End Function
It’s important to keep all of your Windows 2016 RDS servers clean.
Typically a task scheduled daily that deletes the registry bloat is mandatory, especially if a large number of users connect to the RDS.
The effect of registry cleanup will be immediately visible, there’s no need even to restart the Windows Server. For example, the Start Menu should function again right away.
The effects can be also measured. Using a Grafana dashboard in NetEye you can see that the available memory on the RDS server increases after registry cleanup (in this example, the cleanup was started during the night of November 12th):
Also, CPU usage is much more regular: