Programmatically updating local policy in Windows

By: on March 25, 2013

“Group Policy is a feature of the Microsoft Windows NT family of operating systems that control the working environment of user accounts and computer accounts. Group Policy provides the centralized management and configuration of operating systems, applications, and users’ settings in an Active Directory environment…Local Group Policy (LGP) is a more basic version of the Group Policy used by Active Directory.” – Wikipedia

There are various settings in Windows that come under the remit of the group policy. Even for computers that do not belong to an Active Directory domain there are settings that can only be changed via the local group policy.

The graphical editor, gpedit.msc, is pretty easy to use. But what to do when we need to script policy changes?

There is the Group Policy Management Console Class Library, a .NET library, and also a set of Powershell cmdlets based on top of it. However there are frustrating problems with these.

Firstly, they only come packaged with Remote Server Administration Tools, which is a large Windows update. My use case is aimed at targeting policy settings on newly created machines – I don’t want to have to install an 100MB update and then install Windows features – I just want to update a file.

Secondly, and more damningly, this library, coming as it does in the Remote Server Administration Tools, is aimed at managing Active Directory based group policy. It can’t actually modify the local policy – at least so far as I can tell. Documentation is thin on the ground.

Instead, we need to take the following approach.

Firstly, discover which registry keys are changed when you make the change you are interested in using the GUI. Launch gpedit.msc. Additionally, launch ProcessMonitor with these filters active:

  • Process Name is mmc.exe
  • Operation is RegCreateKey
  • Operation is RegDeleteKey
  • Operation is RegSetValue
  • Operation is RegDeleteValue

Secondly, make the changes you want through the GUI, and take note of which registry keys have been changed.

However, you can’t just then apply those same registry changes directly and expect things to work. The group policy doesn’t actually use the registry to store its settings. Rather, it seems to temporarily write them there, and then compile them down to a binary .pol file that lives in C:WindowsSystem32GroupPolicy. We need to trigger this same process.

There exists a Win32 API for making these changes, that I discovered through this blog post, which I am indebted to. Using some hints from this other blog post, I have created a .NET library that allows managed access to this API.

Here is a code sample of using to change one of the RDP settings (my original use case!):

var gpo = new ComputerGroupPolicyObject();
const string keyPath = @"SOFTWAREPoliciesMicrosoftWindows NTTerminal Services";
using (var machine = gpo.GetRootRegistryKey(GroupPolicySection.Machine))
{
    using (var terminalServicesKey = machine.CreateSubKey(keyPath))
    {
        terminalServicesKey.SetValue("SecurityLayer", 00000000, RegistryValueKind.DWord);
    }
}
gpo.Save();

You can get the source or compiled assembly from BitBucket. Please note that this code is not thoroughly tested – I have yet to figure out how to write meaningful unit tests against a library that is so bound up in the OS – watch this space. If and when you find bugs, please raise issues over on BitBucket.

FacebookTwitterGoogle+

20 Comments

  1. Joshua M. Murphy says:

    This is something I’ve been looking for off and on for the past 5 or more years. I was about to resort to writing a tool solely to parse and modify registry.pol directly… but I’ll definitely be giving this a try. I have a few hundred systems with differing local policies that need a few added things set, and I believe you just saved me a lot of hours!

  2. Martin Eden says:

    Glad to be of assistance. 🙂

  3. Blain says:

    Hi, I’m not a coder at all but wondered if you could give me some guidance. This would be very handy but how would I make use of it?

    Thanks!

  4. Martin Eden says:

    This is just a code library. It won’t be of any help to you unless you know how to code in .NET. Sorry – I think the topic of how you could make use of this is too large to address in a blog comment – it involves learning how to write programs.

  5. Blain says:

    Understood thanks anyway!

  6. Ilja says:

    Thank you, exactly what I was looking for. But when I try to execute your usage example, i get a InvalidCastException for the cast at line 127 in GroupPolicyObject.cs
    My VS is not in english but here is a translation:
    Cannot cast a COM object of type LocalPolicy.COM.GPClass to interface type LocalPolicy.COM.IGroupPolicyObject (interface not supported).
    Do you have an idea of why this is happening ?

  7. Martin Eden says:

    Just investigated this. I got this error too and found this StackOverflow answer: http://stackoverflow.com/a/16943296/777939

    The solution is to add a [STAThread] attribute to your Main method.

    Note that you will also need to run as an administrator for accessing and modifying local group policy.

    I will update documentation and try and make the code throw a more helpful exception.

  8. Martin Eden says:

    I have updated the source and added an updated dll to the Bitbucket repository that catches this exception and wraps it in a more friendly explanatory version. I’ve also added a readme to the repo, that tells you how you need to run any programs built with this library.

  9. Ilja says:

    Wow that was fast. Thank you very much, it now works perfectly. Great work 🙂

  10. Phil says:

    Just wanted to say thanks for making this code publicly available! I’ve used it to programmatically enable the ‘DisableForceUnload’ policy as part of an installation and it works a treat.

  11. Ray says:

    This exactly what I’m looking for but my issue is I need it to run using .Net 2.0. I worked on changing the code but I am running into an issue with rewriting the below SafeRegistryHandle code:

    var safeHandle = new SafeRegistryHandle(key, true);
    return RegistryKey.FromHandle(safeHandle);

    I am assuming that I will need to use P/Invoke in order to get the results I would get if I was using SafeRegistryHandle under .Net 4.0.

    Can you give me any advice on this please?

  12. Martin Eden says:

    I’m afraid I don’t know anything about interacting with the registry in .NET 2.0. I’ll be interested to see what you come up with. Feel free to contribute a pull request to the repo.

  13. Priya Ranjan says:

    Actually first i want to hide recycle bin icon form desktop then want to unhide it…. below is my code and it is not working…… i read the path of registry key using Process Monitor as per your blog

    var gpo = new ComputerGroupPolicyObject();
    const string keyPath = @”Software\Microsoft\Windows\Current Version\User Group Objects\{AFC9CDD1-BBBF-4537-A9F7-99F0CCD81971}User\Software\Microsoft\Windows\Current Version\Policies|NonEnum”;

    using (var User = gpo.GetRootRegistryKey(GroupPolicySection.User))
    {
    using (var terminalServicesKey = User.CreateSubKey(keyPath))
    {
    terminalServicesKey.SetValue(“{645FF040-5081-101B-9F08-00AA002F954E}”, 00000001, RegistryValueKind.DWord);
    }
    }
    gpo.Save();

    note—- when i debug then i got this line “using (var User = gpo.GetRootRegistryKey(GroupPolicySection.User))” here User has no value…..

    the above code is not working , i have used localpolicy.dll

  14. Martin Eden says:

    @Priya Ranjan: Are you running as administrator? That’s the only reason I can think of that you wouldn’t be able to get at the user policy section.

    Otherwise I suggest checking out the source code and stepping through to see if you can find the place where LocalPolicy is going wrong and returning NULL when it shouldn’t.

  15. Priya Ranjan says:

    @Martin Eden: yes i am running as administrator. when i debugged the code properly i got sth wrong in this line:-
    “using (var user = gpo.GetRootRegistryKey(GroupPolicySection.User))”, here as per the source code provided by u the return type of the method ‘GetRootRegistryKey’ is ‘RegistryKey’ type. but it is not returning anything. when i am using this statement to print its value messageBox.Show(user.toString());
    it it showing empty.. i have tried your code as well as .dll file, in both case same problem persist. i think due to above bug it is not working…..
    can u plz help me out of this…

  16. Priya Ranjan says:

    @Martin Eden:-And sir one more thing i forgot to mention is—-
    in “local polocy->GroupPolicyObject.cs->GetRootRegistryKey method ” when i am checking one of the statement i.e. return “RegistryKey.FromHandle(safeHandle)”; it is {}.

    Thanks in advance…

  17. Tom says:

    Hi Martin, great work on the library. I have been looking for something like this for ages.

    I need to set the value of a couple of policies, I’ve used the procmon to determine the key that was modified when I used the Group Policy Editor. I’ve put the two paths and values in the code, and when it run it creates the keys in the registry.. however if I open and check the policy with the GPO Editor it doesn’t show anything (is like the key is not configured at all) and indeed I don’t see the system behaving the way it should if I would have set the key.

    Perhaps I’m doing something wrong, or referencing the keys wrong. I appreciate if you could provide some hints.

    The code is simple and similar to your sample:

            var gpo = new ComputerGroupPolicyObject();
                const string keyPath = @"Software\Microsoft\Windows\CurrentVersion\Group Policy Objects\{1E2AC4AE-C9D5-4E5B-B2B9-F4C1FF9040F4}Machine\Software\Policies\Microsoft\Windows\Personalization";
               
                using(var machine = gpo.GetRootRegistryKey(GroupPolicySection.Machine))
                {
                    using(var terminalServicesKey = machine.CreateSubKey(keyPath))
                    {
                        terminalServicesKey.SetValue("LockScreenImage", filename, RegistryValueKind.String);
                        terminalServicesKey.SetValue("NoChangingLockScreen", 1, RegistryValueKind.DWord);
                    }

                }
                gpo.Save();

    and yes I’ve used the [STAThread] attribute for my main, indeed I don’t see any errors or exception and code runs flawlessly, it just doesn’t do what I want it to do 🙂

    cheers

  18. Nice work, Martin! This does what it advertises quite nicely. In particular, good job integrating the .Net RegistryKey classes into this.

    I had no difficulty using this to disable password changes via Ctrl+Alt+Delete.

    +1

    -M

  19. soumya says:

    HI i want to apply group policy settings on some specific user how to do that.. the code is working perfectly for current user i.e for administrator, but how to restrict a specific user on a system…… please help me….

  20. Dilpesh Jain says:

    I am working on implementing user based software restriction policy programmatically for local group policy object. If I create a policy through Domain Controller ,I do have option for software restriction policy in user configuration but in local group policy editor I don’t have option for that. When I look for the changes made by policy applied from Domain Controller in registry, they modify registry values for specific users on path HKEY_USERS(SID of User)\Softwares\Policies\Microsoft\Windows\Safer\Codeidentifiers They also have registry.pol stored in SYSvol folder in Domain Controller. When I make the same changes in registry to block any other application, application is getting blocked. I achieved what I wanted but is it right to modify registry values ?

    If the implementation ever changes, if Windows notices that there are group policy settings in place that aren’t in the actual group policy, will they remove it ?

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*