Forge Home
Premium module


Compliance Enforcement Module for Windows


239 latest version

Version information

  • 1.5.2 (latest)
  • 1.5.1
  • 1.5.0
  • 1.4.0
  • 1.3.0
  • 1.2.3
  • 1.2.2
  • 1.2.1
  • 1.2.0
  • 1.1.2
  • 1.1.1
  • 1.1.0
  • 1.0.7
  • 1.0.6
  • 1.0.5
  • 1.0.4
  • 1.0.3
  • 1.0.2
  • 1.0.1
  • 1.0.0
released Mar 24th 2022
This version is compatible with:
  • Puppet Enterprise 2023.2.x, 2023.1.x, 2023.0.x, 2021.7.x, 2021.6.x, 2021.5.x, 2021.4.x, 2021.3.x, 2021.2.x, 2021.1.x, 2021.0.x, 2019.8.x
  • Puppet >= 6.23.0 < 8.0.0
This module has been deprecated by its author since May 8th 2024.

The reason given was: Replaced by puppetlabs-sce_windows

The author has suggested puppetlabs-sce_windows as its replacement.


puppetlabs/cem_windows — version 1.1.0 Mar 24th 2022


Spec Tests

Table of Contents


The cem_windows module is one of two Compliance Enforcement Modules (CEM). These are supported Puppet modules developed specifically to bring your Puppet Enterpirse (PE) managed nodes under compliance. CEM currently supports CIS (Center for Internet Security) compliance rules, and other compliance frameworks will be added to CEM in the future.

By default, CEM enforces CIS rules for the level 1 server profile.

This README walks you through installing CEM and customizing the configuration settings to suit your own compliance needs. For a list of available parameters, see the CEM reference.

To manage Linux nodes, navigate to cem_linux.


System requirements

cem_windows supports the following operating systems and CIS benchmarks:

Operating system Framework Level Profile
Windows Server 2019 CIS benchmarks v1.2.1 1 Member Server
Windows Server 2016 CIS benchmarks v1.3.0 1 Member Server
Windows 10 Enterprise CIS benchmarks v1.12.0 1 Corporate Enterprise

Note: CEM uses DSC modules and the validation_mode parameter to ensure resources do not get stuck in a "flapping" state. For more information, visit the DSC security policy.

Install CEM with Code Manager

Puppet Forge Premium Content has specific installation instructions found here.


By default, cem_windows enforces CIS rules depending on your operating system — the level 1 Member Server profile is enforced on Windows Server 2016 and Windows Server 2019, and the level 1 Corporate Enterprise profile is enforced on Windows 10.

If you installed CEM with Code Manager and assigned the cem_windows class to a node group in the PE Console, the default profile is automatically enforced. If you installed CEM without Code Manager, add include cem_windows to the profile assigned to your node.

To configure specific benchmarks, you can use Hiera.

Important: CEM's default settings are fully CIS compliant — too much customization can result in your configurations being noncompliant.

Controls Ignored by Default: Controls and are currently ignored by default because of non-idempotent and catalog apply issues. See Controls Ignored By Default Due to Errors for explanation and configuration options.

Finding and setting configuration options

You can find the configuration options for a specific control by referencing the reference file. The reference file is divided into sections, with each section representing a specific benchmark. In those benchmarks, you will see each control listed with several subsections:

  • Parameters:
    • The parameters subsection lists all the configuration options for a control, along with the data type and default value.
  • Config Example:
    • The config example subsection shows a snippet of Hiera that could be used to configure the control.
  • Supported Levels:
    • The supported levels for a CIS control.
  • Supported Profiles:
    • The supported profiles for a CIS control.
  • Alternate Config IDs:
    • The alternate config IDs for a control. Any of these config IDs, along with the full control name, can be used as a key in the control_config hash.
  • Resource:
    • The name of the Puppet resource that enforces the control.

Alternate Config IDs

Due to architectural changes, you can now specify controls in the control_config hash using the full control name, the control number, the normalized control name, or the normalized control number. You cannot mix and match these forms and must pick a single control ID form to use for your config. Full control names and control numbers are copied verbatim from the benchmarks and are case-sensitive. Normalized control names are lowercased and contain only alphanumeric characters and underscores. Normalized control numbers are always prefixed with a c and contain only numeric characters separated by underscores.

Example of Alternate Config IDs:

  • Full control name: (L1) Ensure 'Enforce password history' is set to '24 or more password(s)'
  • Control number: 1.1.1
  • Normalized control name: ensure_enforce_password_history_is_set_to_24_or_more_passwords
  • Normalized number: c1_1_1

Resource Data

The data that drives CEM Windows is located in data/windows/windows/<osmajversion>.yaml. These Hiera files contain resource definitions for each Puppet resource that enforces a control. THIS DATA SHOULD NOT BE MODIFIED. If you need to change the behavior of a control, please use the control_config hash to configure it.

Top-level configuration options

The configuration options are configured at the top-level of the module. If configuring with Hiera, these options will be prefixed with cem_windows::

  • framework - Enum['cis'] - the compliance framework to use. CEM only supports cis. Default: cis.
  • config - Optional[Hash] - where all non-top-level configuration options live. Default: undef.
  • allow_on_domain_controller: - Boolean — If cem_windows detects that it is running on a domain controller, it will not enforce any rules. It will log a warning stating such. This is to prevent accidentally enforcing incorrect compliance settings on domain controllers that could negatively impact an entire domain. Default: true.
  • enable_long_paths - Boolean - Enables long path support via the Windows registry. Setting this to false can cause issues with some of the DSC modules used in cem_windows. Default: true.
  • privileged_user - Optional[String] - If the Puppet agent does not run under a user with local administrator privileges, you need to supply the name of a user that has local administrator privileges. This is used by DSC to enforce state on the machine. Default: undef.
  • privileged_password - Sensitive[Any] - If using a privileged user, this is the password for that user account. Default: undef.
  • allow_local_account_rdp - Boolean - By default, cem_windows disabled remote desktop connection (RDP) access for non-domain accounts. Setting this to true allows local accounts present on the node to make RDP connections to the node. Default: false.

Framework configuration options

These options are available as key-value pairs within the cem_windows::config: hash.

  • control_configs - Optional[Hash] — where all rule-specific configurations live. Default: undef.
  • only - Optional[Array[String]] — takes an array of control class names (manifests/benchmarks/<benchmark>/controls/*.pp) — classes specified here are included in the catalog. Takes precedence over ignore:. Default: undef.
  • ignore - Optional[Array[String]] — takes an array of control class names (manifests/benchmarks/<benchmark>/controls/*.pp). The classes specified here are not included in the catalog. If only: is specified, this option does nothing. Default: undef.

CIS-specific configuration options

These options are available as key-value pairs within the cem_windows::config: hash. These options are only applicable to the CIS compliance framework.

  • profile - Optional[Enum['member_server', 'corporateenterprise']] — the name of the benchmark profile. corporateenterprise is only supported on Windows 10. Default for Windows Server: member_server, Default for Windows 10: corporate_enterprise.
  • level - Optional[Enum['1', '2']] — the name of the profile level. CEM only supports 1. Default: 1.

Control classes

Control classes (manifests/benchmarks/<benchmark>/controls/*.pp) are the interfaces that configure rule settings. Each control class accepts the following two parameters:

  • The $enforced (Boolean) parameter — this toggles whether the included code in the manifest is executed.
  • The $config parameter — this holds the configuration options for a control class as keys in the hash. You set the $config parameter based on values from the control_configs hash. Each top-level key is a control class name (sans path and .pp suffix) and the value of that key is a hash. The keys of the sub-hash map 1 to 1 with the configuration options available in the specific control class.

For example, a control class would looks like:

# @summary CIS 1.2.1 Windows Server 2019 17.1.1 L1 Ensure Audit Credential Validation is set to Success and Failure
# @param [Boolean] enforced
#   If true, the control will be enforced
# @param [Hash] config
#   Options for the control
# @option config [Boolean] success
#   Defaults to true
# @option config [Boolean] failure
#   Defaults to true
class cem_windows::benchmarks::cis::controls::w2019::c17_1_1 (
  Boolean $enforced = true,
  Hash $config = {},
) {

And the Hiera file would look like:

      success: true
      failure: true

For a list of control classes and their configuration options, see the reference.

Configuration Examples

Running DSC resources as a specific user

DSC requires local administrator privileges to modify Windows resources. Normally, the Puppet agent runs under a user account with these permissions. However, if the Puppet agent on a node does not have local administrator permissions you can configure the username and password of a user account that does using Hiera. For example:

# control-repo/data/nodes/
cem_windows::privileged_user: <user name>
cem_windows::privileged_pass: <user password>

Allowing local accounts to access nodes

To configure a node to allow a local user account to access the node with RDP, set the top-level option allow_local_account_rdp to true. For example:

# control-repo/data/nodes/
cem_windows::allow_local_account_rdp: true

Enforcing specific rules

To configure CEM to enforce only enforce specific rules, you can use the only key. For example:

# control-repo/data/nodes/
cem_windows::framework: 'cis'
  profile: 'member_server'
  level: '1'
    - 'c18_9_97_1_1'
    - 'c18_9_97_1_2'

Ignoring specific rules

To configure CEM to ignore specific rules, you can use the ignore key. For example:

# control-repo/data/nodes/
cem_windows::framework: 'cis'
  profile: 'member_server'
  level: '1'
    - 'c18_9_97_1_1'
    - 'c18_9_97_1_2'

Note: The only key and the ignore key are mutually exclusive, with only taking precedence. This means that if you specify both keys, CEM does not use the value of the ignore key is not used and only enforces the rules specified with only.

Configuring individual rules

You can customize most rules. To configure these, use the control_configs key and supplying it with a Hash value:

<recommendation class name>:
  <recommendation class param>: <value>

For example, if you want to configure the rules used in the previous examples, it would look like:

# control-repo/data/nodes/
cem_windows::framework: 'cis'
  profile: 'member_server'
  level: '1'
      allowbasic: '0'
      allowunencryptedtraffic: '0'

Renaming Administrator and Guest accounts

To rename the local Administrator account to user_1, use the following configuration.

Important: The Administrator account is renamed to magic by default.

# control-repo/data/nodes/
      value: 'user_1'

To rename the local Guest account to user_2, use the following configuration.

Important: The Guest account is renamed to pumpkin by default.

# control-repo/data/nodes/
      value: 'user_2'

Known Issues

cem_windows currently has the following limitations:

  • Some controls can fail scans. Each of these controls is enforced by CEM Windows. The following controls have been known to fail scans when scanned with Comply:
    • 1.1.5 - Windows Server 2016 & Windows Server 2019
    • 1.1.6 - Windows Server 2016 & Windows Server 2019
    • - Windows Server 2016
    • 18.2.1 - Windows Server 2019
    • 18.4.1 - Windows Server 2016 & Windows Server 2019
    • 18.4.8 - Windows Server 2016 & Windows Server 2019
    • 18.4.9 - Windows Server 2016 & Windows Server 2019
    • 18.4.12 - Windows Server 2016
    • - Windows Server 2019
    • - Windows Server 2016 & Windows Server 2019
    • - Windows Server 2019
    • - Windows Server 2016 & Windows Server 2019
  • Puppet runs are not idempotent. If you see DSC resources showing corrective changes in a Puppet run, for example, Unknown feature "custom_isync", it means that you are not running the correct version of Puppet. cem_windows requires Puppet Agent >= v6.23 < 7.0 or >= 7.8.
  • Puppet agent does not upgrade using the puppetlabs-puppet_agent module. When you upgrade the Puppet agent upgrades, make sure you reboot your machine.
  • You cannot RDP to nodes. By default, users that are members of the groups Guests and local accounts will not be able to log in. To override this behavior, set the cem_windows::allow_local_account_rdp parameter to true.
  • Non-admin users cannot log in to nodes. By default, the event Log does not clear events. When the event log of a node is full , only administrators can log in. To clear the event logs manually, find the specific recommendation in your compliance framework and configure the setting. This behavior is set by the registry key being 0 (HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\EventLog\Application:Retention).
  • WinRM is not disabled. The WinRM service is required for the DSC modules and cannot be disabled.

Controls Ignored By Default Due To Errors

  • (Ensure 'Accounts: Administrator account status' is set to 'Disabled') - Can cause non-idempotent runs. Can cause puppet run failures when running puppet manually while logged in as administrator.
  • (Configure 'Accounts: Rename administrator account') - Can cause non-idempotent runs. Can cause puppet run failures when running puppet manually while logged in as administrator.

To enable controls ignored by default create an ignore config that doesn't include them. This for example will only ignore control 1.1.1, thus overriding the default ignore list:

    - 'c1_1_1'

This will remove all controls from the ignore list:

  ignore: []