Cleaning up corrupt Registry.pol files with Powershell

One ongoing issue that can occur across an predominately Windows/Group Policy heavy enterprise environment is the corruption of the Registry.pol file located in %windir%\system32\Group Policy\Machine\. This file contains all the machine-based Group Policy settings in Registry format and are loaded at Operating System startup.

For reasons not even known by Microsoft it seems, this file can occasionally get corrupt and centrally defined Group Policies are no longer updated/kept in sync.

So in a effort to be able to clean up such a corruption at scale, I created a Powershell script that:

  1. Takes a array of machines as input
  2. Confirms they are reachable over the network
  3. Confirms that it has permissions to the location of Registry.pol
  4. Check the Date Modified tag of the file and if older than 1 day (good sign of corruption), delete the file and force a Group Policy refresh

One of the caveats to this process was while there a many more cmdlets in Powershell V3+ that I could leverage, it still had to support Windows 7 machines at the time of writing and therefore leverage much less cleaner ways to kick of a Group Policy refresh.

Without further ado…


<#
.Synopsis
Clean up machines with bad (old/corrupt) machine Registry.pol files
.DESCRIPTION
Taking a array as input, this cmdlet assists in keeping machines in a healthy state to accept Group Policy driven changes
by confirming the last modified date of the machines Registry.pol and if older than a day , remove it, (or doesn't exist)
followed by a forced Machine Policy update.
To work against older WMF/Powershell environments, invoke-command + invoke-gpupdate have been avoided.
.EXAMPLE
Repair-RegistryPol -Computers workstation1,workstation2
.EXAMPLE
Repair-RegistryPol -Computers (Import-Csv lotsofworkstations.csv)
.NOTES
Version: 1.0
Author: James Pettigrove
#>
[CmdletBinding()]
Param (
[Parameter(Mandatory=$True,Position=0)]
$Computers
)
foreach ($computer in $Computers)
{
if (Test-NetConnection ComputerName $computer Hops 1 InformationLevel Quiet ErrorAction SilentlyContinue WarningAction SilentlyContinue)
{
if (Test-Path Path \\$computer\c$\Windows\System32\GroupPolicy\Machine\Registry.pol ErrorAction SilentlyContinue WarningAction SilentlyContinue)
{
$regpol= Get-Childitem \\$computer\c$\Windows\System32\GroupPolicy\Machine\Registry.pol
if ($regpol.LastWriteTime -lt (get-date).AddDays(-1))
{
Write-Host "$computer Registry.pol file is old ("$regpol.LastWriteTime"), deleting and forcing a GPUpdate" ForegroundColor Magenta
Remove-Item $regpol
Invoke-WmiMethod Name create Path win32_process ArgumentList "gpupdate /target:Computer /force /wait:0" AsJob ComputerName $computer | out-null
}
else
{
Write-Host "$computer Registry.pol file is healthy ("$regpol.LastWriteTime")" ForegroundColor Green
}
}
elseif (Test-Path Path \\$computer\c$\Windows\System32\GroupPolicy\Machine\ ErrorAction SilentlyContinue WarningAction SilentlyContinue)
{
Write-Host "$computer doesn't have a Registry.pol file, forcing a GPUpdate" ForegroundColor Magenta
Invoke-WmiMethod Name create Path win32_process ArgumentList "gpupdate /target:Computer /force /wait:0" AsJob ComputerName $computer | out-null
}
else
{
Write-Warning "$computer does not have c:\Windows\System32\GroupPolicy\Machine\ or you don't have access"
}
}
else
{
Write-Warning "Unable to find or contact $computer"
}
}

Would love to see what improvements the readers out there could make. Maybe make use of job batching to do it in a much more parallel fashion? Let me know if you do give it a try in your environment.

James Written by:

24 Comments

  1. DeployGuy
    24/01/2019
    Reply

    I just ran into one of these corrupt registry.pol instances and found your contribution while searching how to repair the damage. Nice work, very clever on using the date of the file as a probable indicator of file corruption! Using the immediate script function in SCCM I can query all my machines looking for this file and whether it is over one day old and perhaps detect machines that have a problem before the user even knows they have a problem.

    • 25/01/2019
      Reply

      Hey DeployGuy, glad you found my post and it was helpful. It certainly is perfect to slot into a compliance rules in SCCM as a self-healing/automation function to keep the EUC fleet humming 🙂

  2. Allan Asante
    17/04/2019
    Reply

    Hi James, this is great and just what I need, but i’m a bit of a newbie with powershell .. having issues with the format of the csv file containing all computers … can you help?

    • 26/05/2019
      Reply

      Hey Allan, I would be honoured to help out. This particular script requires a no frills CSV file. Just one row, without headers, of the DNS/FQDN or IP address of the workstations you would like this to be ran against.

  3. Tom Wiggins
    25/07/2019
    Reply

    Hi James. Bit of a powershell & sccm noob here. To deploy this in a compliance rule in SCCM as a self-healing/automation function do you need to modify the script read in the machine detail? Also if I create this powershell script as a package and deploy to a collection as required how does the script know which machine its running on.

  4. Gael
    20/05/2020
    Reply

    Dear James,
    With this Co-Vid confinement, we just discovered that some computers are facing registry.pol corruption.
    Our main problem is that we have a thousand of people working with laptop and DirectAccess. So, you can imagine the problem once policies are not applying (and DA policies lost). The user cannot join our corporate network anymore…

    I will test and implement your script in my SCCM compliance !! I hope it will work and save us hours of work !

    In advance (because I am a positive IT Guy, lol) GREAT JOB, thanks again for your contribution

    • 20/05/2020
      Reply

      Hey Gael,

      Comments like these absolutely warms my heart. Please reach out if you run into any brick walls integrating this as SCCM Compliance Item/Baselines.

  5. Gael
    29/06/2020
    Reply

    Hi James,
    I have tested and your script works like a charm !
    I wanted also to tell you that we have found our culprit for corrupting registry.pol …

    We works with WFBS from Trend-Micro and since teleworking we noticed a lot of corruptions. A colleague of us had the brilliant idea to create a whitelisting rule for this file … and .. tatataaaa .. For a month, we don’t have any issue !!

    I keep finger-crossed and I will give it for solve in a month if no corruption is back !

    Enjoy !!

  6. PSNoob
    09/07/2020
    Reply

    Hi James, your script is working perfectly on individual computers, but I get an error when using a csv file. The script outputs Unable to find or contact @{Hostname1=Hostname2} and on the next line @{Hostname1=Hostname3} and so on.

    It’s got to be such a simple issue but I don’t see anything wrong in the script. Thanks so much for this!

    • 14/12/2020
      Reply

      Hey PSNoob,

      Sounds like your CSV isn’t structured correctly. Ensure there is a column that has a header that matches the variable used as a switch (in this case “Computers”). That or remove all columns and headers in your CSV so it only has the computer name

  7. Ahmad
    26/07/2020
    Reply

    Thanks for the script, a little help please.
    where do i run the script? on Domain controller or any machine in the network?
    appreciate your help.

    • 14/12/2020
      Reply

      Hey Ahmad,

      It can be run from anywhere, so long as the user and host has network connectivity to the endpoints you are targeting with the script and the user has the appropriate permissions

  8. Chad
    12/12/2020
    Reply

    Are you able to run this script across your entire system? looks like its only one machine

    • 14/12/2020
      Reply

      Hi Chad,

      You certainly can. As the description states, it takes a array as input. If you are running in a Windows Domain, you could make use of the Get-ADComputer cmdlet to build your array. An example of this would be:

      Repair-RegistryPol -Computers (Get-ADComputer -Filter {OperatingSystem -Like “Windows 10*”}).Name

      This will grab all computers in AD with a Operating System of Windows 10 and select’s the Name property to passthrough

      • Chad
        15/12/2020
        Reply

        I am not too familiar with powershell, where exactly would I place this array in the script?
        Repair-RegistryPol -Computers (Get-ADComputer -Filter {OperatingSystem -Like “Windows 10*”}).Name

        • 05/01/2021
          Reply

          Hey Chad,

          (Get-ADComputer -Filter {OperatingSystem -Like “Windows 10*”}).Name is your array.

          In the above example, the cmdlet gets all AD Objects that are computers with the operating system Windows 10 and returns ONLY the Name value.

          Think of an array as a table in Excel. We’ve just been a bit fancy and utilized an existing Powershell cmdlet, to get us the table data we require and inject that as answer to our original Powershell cmdlet’s parameter

  9. Danny Jeronimo
    23/04/2021
    Reply

    I get this when I try to run the script. WARNING: Unable to find or contact @{Computers=nameofcomputer}

    All I have in the CSV is a column of computers names, no headers.

  10. Lee Millward
    27/05/2021
    Reply

    This Script has been a lifesaver for when unpicking SCCM windows update issues. I see some people have managed to incorporate this into an SCCM baseline configuration. If they can give me any tips on how to do this I would be very greatful.

    • 31/05/2021
      Reply

      Hey Lee,

      Glad to hear you have found it useful. In terms of utilizing this script within a SCCM baseline; You will need to unpick the script and separate into two parts; detection and remediation.

      Generally, the detection scripts need to be binary in their answer (yes/no, true/false etc…). So for the detection it would look something like:

      $regpol= Get-Childitem $env:SystemRoot\System32\GroupPolicy\Machine\Registry.pol
      if ($regpol.LastWriteTime -lt (get-date).AddDays(-1))
      {
      echo $false
      }
      else
      {
      echo $true
      }

      You would set the Configuration Item to to say the value returned by the script must be true.

      Your remediation script would look something like:

      Remove-Item $env:SystemRoot\System32\GroupPolicy\Machine\Registry.pol
      Invoke-WmiMethod -Name create -Path win32_process -ArgumentList “gpupdate /target:Computer /force /wait:0” | out-null

      Note, in both examples of the above, slight modifications were made due to not been required to parse the paths as UNC paths (as SCCM will be running via the agent locally on each workstation)

  11. Lee Millward
    08/06/2021
    Reply

    Amazing James, thankyou

  12. William
    08/09/2021
    Reply

    Hi, I notice your script only addresses the Machine\Registry.pol However, I find the User\Registry.pol also causes problems when it is not current.

  13. Joseph
    10/03/2022
    Reply

    So I broke this up for SCCM and ran it without remediation and I noticed that ($regpol.LastWriteTime -lt (get-date).AddDays(-1)) always returned non-compliant even on machines where the file had been modified on the current date. I had to change to +1 to get it to return properly. Am I doing something wrong or is this script going to delete every registy.pol file it sees?

  14. Paresh
    19/10/2022
    Reply

    Hello,

    Im how do i reference the CSV. Not sure where t put it and how to reference it. Please assist

  15. robertmakaimckechnie
    01/03/2023
    Reply

    Hi I have the same issue above unable to find or contact @{computers=D-****}
    .csv only has column header = Computers and the the one computer entered below. Tried fqdn, IP but still the same error, I can contact through cm by the computer name or IP, Can you advice please, Thanks Robert

Leave a Reply to Lee MillwardCancel reply