Manage Local Group Policy Objects from PowerShell and Desired State Configuration

Ever since DSC was first released, people have been asking how they can use it to manage user-specific settings. For the most part, the answer has been: don’t do that. DSC resources execute as LocalSystem, and are intended to manage system-wide settings.

However, that’s not the whole story. The local Group Policy objects on a computer can be treated as a system-wide setting, and can also be used to enforce user-specific policies. There’s a problem, though: those settings tend to be stored in the Administrative Templates section of the registry, which is saved in a registry.pol file on disk. There are no command-line utilities or APIs in Windows for reading or writing these registry.pol files, and the GroupPolicy PowerShell module cmdlet Set-GPRegistryValue only works for domain GPOs, not local. I’d solved the problem of managing Registry.pol files from a script ages ago, first as a VBScript, and later in C#. The .NET code wasn’t terribly user-friendly compared to a PowerShell cmdlet, but it worked, and I used it extensively in my previous job.

Recently, when this question of managing user-specific settings and/or local GPOs came up again, I decided to write some PowerShell wrappers around that C# class, and DSC resources as well.

The result is the new PolicyFileEditor module, which can be found on GitHub, or via PowerShellGet. The module exposes 4 commands:

Get-PolicyFileEntry [-Path] <string> [-Key] <string> [-ValueName] <string> [<CommonParameters>]
Get-PolicyFileEntry [-Path] <string> -All [<CommonParameters>]

Set-PolicyFileEntry [-Path] <string> [-Key] <string> [-ValueName] <string> [-Data] <Object> [-Type <RegistryValueKind>] [-NoGptIniUpdate] [-WhatIf] [-Confirm] [<CommonParameters>]

Remove-PolicyFileEntry [-Path] <string> [-Key] <string> [-ValueName] <string> [-NoGptIniUpdate] [-WhatIf] [-Confirm] [<CommonParameters>]

Update-GptIniVersion [-Path] <string> [-PolicyType] <string[]> [-WhatIf] [-Confirm] [<CommonParameters>]

There are also two DSC resources (cAdministrativeTemplateSetting and cAccountAdministrativeTemplateSetting) which are wrappers around the *-PolicyFileEntry commands for the various local GPOs.

We immediately put this to good use in one of our dev\test cloud environments were we kept accidentally kicking each other out of RDP sessions:

cAdministrativeTemplateSetting AllowMultipleRdpSessions
    Ensure       = 'Present'
    PolicyType   = 'Machine'
    KeyValueName = 'SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services\fSingleSessionPerUser'
    Type         = 'DWord'
    Data         = '0'

About Dave Wyatt

Microsoft MVP (PowerShell), IT professional.
This entry was posted in PowerShell and tagged , . Bookmark the permalink.

16 Responses to Manage Local Group Policy Objects from PowerShell and Desired State Configuration

  1. Thank you for this code!
    The DSC Resource works perfectly to set some machine policies.

    I do however run into a bug somewhere when I run the get-dscconfiguration on a server that has a administrative templatesetting defined.

    cAdministrativeTemplateSetting ‘test’
    PolicyType = ‘Machine’
    KeyValueName = ‘Software\Policies\Microsoft\Windows\AppCompat\DisableUAR’
    Data = ‘1’
    Ensure = ‘Present’
    Type = ‘DWord’

    Set, and Test work perfectly only get gives the following error:

    Job {50582816-4D6A-11E5-80BE-00155D057002} :
    Message Unable to cast object of type ‘System.UInt32’ to type ‘System.Collections.IList’.
    Parameter name: value
    HResult -2147024809


  2. Mike says:

    Are there any examples on controlling Local Policies? The example above, looks like a Registry Resource.


    • Dave Wyatt says:

      That’s all the Administrative Templates section of Group Policy is, really: registry keys that are applied and enforced over time by the GP engine. The trick is to figure out which registry keys go with which policy in the GUI, and for that (on Windows Vista or later), you want to look in the .admx and .adml files that you’ll find under C:\Windows\PolicyDefinitions. For example, let’s say we’re looking at the GUI, and find Computer Configuration\System\Login\Turn off Windows Startup Sound. To figure out the registry key, I’ll search all of the .adml files under C:\Windows\PolicyDefinitions\en-us for the words “Turn off Windows Startup Sound” (easily accomplished with the Select-String cmdlet), and I find this:

      string id=”DisableStartupSound”>Turn off Windows Startup sound

      Now, the next step is to search the corresponding ADMX file for the string ID, in this case, DisableStartupSound. So I open up Logon.admx (because the string ID was found in Logon.adml), and read. In this case, the key is “Software\Microsoft\Windows\CurrentVersion\Policies\System”, it’s a Machine policy setting, and the value name is “DisableStartupSound”, which is a DWORD value taking 1 or 0.

      Reading the ADMX files can sometimes be very simple, like in this instance, and sometimes a bit tricky (for settings that involve multiple registry values), but you’ll get the hang of it, and the internet is always there to help. 🙂

      Liked by 1 person

      • Mike says:

        Thanks for the added background!

        If I were to write something like this:

        cAdministrativeTemplateSetting RemoveMultipleRdpSessions
        Ensure = ‘Absent’
        PolicyType = ‘Machine’
        KeyValueName = ‘SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services\fSingleSessionPerUser’
        Type = ‘DWord’
        Data = ‘0’

        That would, only remove the registry key that that location, right? Is there a way to set a policy, on the local machine to, disabled or not configured with this resource?


        • Dave Wyatt says:

          If you do Ensure = ‘Absent’, then you’re removing that registry value from the policy file (which generally means “not configured”). If you did Ensure = ‘Present’, and set the Data to ‘0’, then it would be like configuring the policy and setting it to Disabled.


      • chlsmith says:

        This is great to know. Do you know where I can find the same kinds of registry entries to correlate with the “Windows Settings -> Security Settings” stuff in GPO? From what I can find, it looks like it may just go all over the registry. It has to be written down somewhere, though, right?


        • Dave Wyatt says:

          I wouldn’t try to modify that sort of thing directly in the registry, even if it is technically stored there. That’s meant to be managed via APIs, frequently stored in the SAM database, which you really don’t want to try to mess with directly. (Bunch of undocumented binary blobs.)


          • chlsmith says:

            Hmm…….reasonable, but what about “GPO is dead”?

            And really, if I have a non-domain-joined Windows server, how can I replicate these options?


            • Dave Wyatt says:

              Write DSC resources that wrap those other APIs. You could have a resource to assign user rights, one to set password policies, etc. That way you’d be changing those settings through supported methods instead of trying to dive right into the registry.

              Liked by 1 person

  3. js2010 says:

    Can you use the grouppolicy powershell module that comes with rsat to modify local group policy?


  4. Any chance this functionality is going to be adopted in to the PSDscResources module? I’m very glad to use this module just some people in my office are skeptical of using modules not published by Microsoft directly.


    • Dave Wyatt says:

      No idea. Technically, Microsoft has taken and rebranded some of this code before (when it was originally shared on the TechNet gallery), but I don’t know if they have any intention of doing that with DSC resources.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s