In this post, I will discuss a PowerShell script that helps you find and delete unlinked Group Policy Objects (GPO), also known as orphaned GPOs. Orphaned GPOs are not linked to any Active Directory sites, domains, or organizational units (OUs). This can cause various problems. Using PowerShell, it is easy to create reports of unlinked GPOs, back them up, and eventually delete them.
Avatar

Update: The method discussed in this post only works for GPOs that are linked to OUs because Get-GPOReport isn't able to handle Site-linked GPOs. Don't use the script if you have Site-linked GPOs!

What causes orphaned GPOs?

Many admins extensively test GPOs before implementing them. Sometimes they simply unlink a test GPO instead of deleting it. Policies continuously change in most networks. If certain policies become obsolete, admins tend to just unlink them because the corresponding policies might become relevant again at a later time.

In my experience, this happens quite often, and many admins don't really care about unlinked GPOs. They just create new GPOs over and over again until nobody has any idea which GPOs are still used and which became orphaned years ago.

After years of changes, depending on the complexity of your environment, you may have hundreds or even thousands of unlinked GPOs. This leads to performance issues and messes up your GPO infrastructure. In can confuse adminsand lead to configuration errors.

Who wants to go through all these GPOs in the Group Management Console (GPMC) to find orphaned GPOs and delete them? Fortunately, PowerShell makes it relatively easy to automate the GPO cleanup process or just send notifications if GPOs become orphaned.

Finding unlinked GPOs with PowerShell

If you link GPOs to Sites you couldn’t use this method, as it is described in this post. If you want to use this method, you have to exclude Site linked GPOs based on a filter that identify those. It could be based on a Naming convention.

To find unlinked GPOs, we will use the Get-GPO and Get-GPOReport commands, which belong to the Group Policy module that is a part of RSAT. You can import the module with this command:

Import-Module GroupPolicy

To retrieve all GPOs, we use the -All parameter and use Get-GPOReport to cast them to an XML object. We pipe the result to Select-String, which finds us all lines without "<LinkTo>".

Get-GPO -All | Sort-Object displayname | Where-Object { If ( $_ | Get-GPOReport -ReportType XML | Select-String -NotMatch "<LinksTo>" ) {$_.DisplayName } }

The following command creates a list of all unlinked GPOs:

Get unlinked GPOs

Get unlinked GPOs

To log these GPOs to a text file, you just have to add this line after $_.DisplayName:

| Out-File "c:\admin\GPOBackup \UnLinkedGPOs.txt" –Append

We will use it later to log GPOs.

Deleting GPOs with PowerShell

Because you might not want to delete GPOs before creating a backup, we need a variable for our backup location.

$BackupPath = "c:\admin\GPOBackup"

The next command creates the backup:

Backup-GPO -Name $_.DisplayName -Path $BackupPath

To create a report of unlinked GPOs, we have to use Get-GPOReport again. However, instead of using XML, we will set the ReportType to HTML.

Get-GPOReport -Name $_.DisplayName -ReportType Html -Path "c:\admin\GPOBackup\$Date\$($_.DisplayName).html"

As the last step, we delete those GPOs using the Remove-GPO cmdlet:

$_.Displayname | Remove-GPO -Confirm

Let´s put all of these parts together. The only thing I added is a $Date variable to identify backups from different days.

Import-Module GroupPolicy
$Date = Get-Date -Format dd_MM_yyyy
$BackupPath = "c:\admin\GPOBackup\$Date"
if (-Not(Test-Path -Path $BackupPath)) 
{ New-Item -ItemType Directory $BackupPath -Force}
Get-GPO -All | Sort-Object displayname | Where-Object { If ( $_ | Get-GPOReport -ReportType XML | Select-String -NotMatch "<LinksTo>" )
 {
   Backup-GPO -Name $_.DisplayName -Path $BackupPath
   Get-GPOReport -Name $_.DisplayName -ReportType Html -Path "c:\admin\GPOBackup\$Date\$($_.DisplayName).html"
   $_.DisplayName | Out-File "c:\admin\GPOBackup\$Date\UnLinkedGPOs.txt" -Append
   $_.Displayname | remove-gpo -Confirm
   }
}
Example of GPO backup and reports

Example of GPO backup and reports

You can run this script as a scheduled task to automate GPO cleanup.

If you simply want to create a report without automatically removing GPOs, you can remove $_.Displayname | remove-gpo –Confirm from the command. You can then check your backup location to take a closer look at orphaned GPOs. You can also send e-mails that inform you about new, unlinked GPOs. This is accomplished with the Send-MailMessage cmdlet that I added at the end of the script.

Subscribe to 4sysops newsletter!

Import-Module GroupPolicy
$Date = Get-Date -Format dd_MM_yyyy
$BackupPath = "c:\admin\GPOBackup"
if (-Not(Test-Path -Path $BackupPath)) 
{ New-Item -ItemType Directory c:\admin\GPOBackup -Force}
Get-GPO -All | Sort-Object displayname | Where-Object { If ( $_ | Get-GPOReport -ReportType XML | Select-String -NotMatch "<LinksTo>" )
 {
   $_.DisplayName | Out-File $BackupPath + "UnLinkedGPOs$Date.txt" -Append
   #Set E-mail variables.
   $EmailFrom = "gporeport@domain.com"
   $EmailTo = "Tim@domain.com"
   $Subject = "Unlinked GPO Report"
   $Body = (Get-Content $BackupPath\UnLinkedGPOs$Date.txt | Out-String)
   $SMTPServer = "smtp.domain.com"
   #Send Email
   Send-MailMessage -Subject $Subject -Body $Body -SmtpServer $SMTPServer -Priority High -To $EmailTo -From $EmailFrom
  }
}

Conclusion

I highly recommend cleaning up your Group Policy environment regularly to reduce complexity and simplify the work of Group Policy admins for your network. I hope my script helps you automate this task!

avatar
10 Comments
  1. Avatar
    Vincent 6 years ago

    Hello,

    I ran this script (without deleting GPOs), it reported some GPOs as non-linked, but in fact, those are linked to AD sites, not OU.

    So if had included the delete command, i would have deleted active GPOs …

    Would you mind having a look at it please ?

     

  2. Avatar Author

    Hi Vincent,

    using Get-GPOReport doesn´t include Sites. You can use the mentioned method, if you linking GPOs to a domain or OUs. If you want to use it, you have to exlclude site-linked GPOs based on naming or searching for those and compare them.

     

    avatar
    • Avatar
      Vincent 6 years ago

      Hi,

      Thanks for your reply.

      I was asking just to be sure i was using your script the right way.

      Regards,

       

  3. Avatar
    devi 4 years ago

    When I use the Following script it giving an error

    "Cannot validate argument on parameter 'Name'. The argument is null or empty"

    $Date = Get-Date -Format dd_MM_yyyy
    $BackupPath = a"E:GPOBackup$Date"
    $GPOs=get-content "E:GPOGPONames.txt"
    if (-Not(Test-Path -Path $BackupPath))
    { New-Item -ItemType Directory $BackupPath -Force}
    foreach ($gpo in $gpos)
     {
       Backup-GPO -Name $GPO -Path $BackupPath
       Get-GPOReport -Name $GPO -Domain test.com -Server test1.com -ReportType Html -Path "E:GPOBackupgporeport.html"
       $gpo | Out-File "E:GPOBackupbackupgpo.txt" -Append
      }

    I am giving input file  as I need particular GPOs needs to back up not all in domain

    • Avatar

      Hi Devi

      Could you please check the current content of E:\GPO\GPONames.txt.
      From the error message the only inference that can be drawn is, it contains some whitespace at the beginning of the file.

  4. Avatar
    devi 4 years ago

    Thanks.. its working now

     

    avatar
  5. Avatar
    Gururaj 4 years ago

    where the links are preserved if I use -keeplinks when the GPO itself is deleted.

  6. Avatar
    Bernd 4 years ago

    HRESULT: 0x80072035)

    by accident i deleted 'default domain policy'

    orphaned and missing.

  7. Avatar
    Simon Smith 3 years ago

    There is an enterprise level tool called Gytpol Validator which does this on all endpoints in an organization

  8. Avatar
    Thomas 3 years ago

    You said this doesn't include site-linked GPOs. Someone please demonstrate HOW to find site-linked GPOs, so that we can exclude them. How do we know a GPO is "site-linked?"

    Thanks.

Leave a reply

Your email address will not be published. Required fields are marked *

*

© 4sysops 2006 - 2023

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account