GPO Export to CSV for Import to Different System Later

Trying to figure out a way to export all GPO’s to a CSV so they can at some point be imported in to another domain as part of an MSP’s standard deployment methods via script instead of GUI.

Install-Module -Name GPRegistryPolicy -force
get-gpo -all | select DisplayName

#Let’s say for this example there are only three GPO’s
GPOTest_01
GPOProd_01
GPOFinance_01

Loop through all GPO’s
ForEach($GPO in $GPOList)
{
$gpoGuid = (Get-GPO -Name $GPO).Id.ToString()
$domainController = ‘DC01’
$domainName = ‘mycorp.com
$registryPolPath = “\$domainController\sysvol$domainName\Policies{$gpoGuid}\Machine”
Get-ChildItem -Path $registryPolPath
$RegPolPath = Join-Path -Path $registryPolPath -ChildPath ‘registry.pol’
Parse-PolFile -Path $regPolPath
$regKeyInfo = Parse-PolFile -Path $regPolPath
}

CSV Output:
“DisplayName”,“KeyName”,“ValueName”,“ValueType”,“ValueData”
“GPOTest_01”,“HKLM\Software\Policies\Microsoft\W32time\Parameters”,“NtpServer”,“REG_SZ”,“time.windows.com,0x9”
“GPOTest_01”,“HKLM\Software\Policies\Microsoft\W32time\Parameters”,“Type”,“REG_SZ”,“NT5DS”

"KeyName : Software\Policies\Microsoft\W32time\Parameters
ValueName : NtpServer
ValueType : REG_SZ
ValueLength : 42
ValueData : time_windows_com,0x9

KeyName : Software\Policies\Microsoft\W32time\Parameters
ValueName : Type
ValueType : REG_SZ
ValueLength : 12
ValueData : NT5DS

$gpoName = ‘GPOTest’
New-GPO -Name $gpoName -Comment ‘GPO Test Automation’
$gpRegParams = @{
Name = $gpoName
Key = “HKLM$($regKeyInfo.KeyName)”
ValueName = $regKeyInfo.ValueName
Type = $regKeyInfo.ValueType
Value = $regKeyInfo.ValueData
}
Set-GPRegistryValue @gpRegParams

Please format your code as code.

I did not try to digg in your code because it is not formatted … so here are some general tipps:

For the same domain you could use

and

To circumvent this limitation you could use a script to use Backup-GPO to backup each single GPO into a separate folder. This way you could use the folder names for the import and creation of the GPOs to the target domain.

When you post code or sample data or console output please format it as code using the preformatted text button ( </> ). Simply place your cursor on an empty line, click the button and paste your code.
Thanks in advance

Please got back and edit both of your posts to fix the formatting of the code. Do not create a new one!

Only missing one item still which is being able to add DisplayName in the first column of the CSV using the value stored in $GPOName

#Initial Setup
Install-Module -Name GPRegistryPolicy -force
$domainController = 'DC01'
$domainName = 'mycorp.com'

$GPOList = get-gpo -all | sort DisplayName

$Result =
foreach ($DisplayName in $GPOList) {
  $GPOName = $DisplayName.DisplayName
  $gpoGuid = (Get-GPO -Name $GPOName).Id.ToString()
  $registryPolPath = "\\$domainController\sysvol\$domainName\Policies\{$gpoGuid}\Machine"
  $RegPolPath = Join-Path -Path $registryPolPath -ChildPath 'registry.pol'
  if (Test-Path -Path $RegPolPath) {
    $regKeyInfo = Parse-PolFile -Path $regPolPath | Select-Object -Property DisplayName,KeyName,ValueName,ValueType,ValueLength,ValueData
    write-output $regKeyInfo
  }
}
$Result | Export-Csv -Path 'c:\ADBackup\GPOSettings.csv' -NoTypeInformation -Delimiter ','
Get-Content -Path 'c:\ADBackup\GPOSettings.csv'

You could use a calculated property or a [PSCustomObject] to add an additional property of your choice.

#Initial Setup
Install-Module -Name GPRegistryPolicy -force
$domainController = 'DC01'
$domainName = 'mycorp.com'

$GPOList = get-gpo -all | sort DisplayName

$Result =
foreach ($DisplayName in $GPOList) {
  $GPOName = $DisplayName.DisplayName
  $gpoGuid = (Get-GPO -Name $GPOName).Id.ToString()
  $registryPolPath = "\\$domainController\sysvol\$domainName\Policies\{$gpoGuid}\Machine"
  $RegPolPath = Join-Path -Path $registryPolPath -ChildPath 'registry.pol'
  if (Test-Path -Path $RegPolPath) {
    $Object = [PSCustomObject]@{
      'DisplayName' = $GPOName
      'KeyName'     = Parse-PolFile -Path $regPolPath | Select-Object -Property KeyName
      'ValueName'   = Parse-PolFile -Path $regPolPath | Select-Object -Property ValueName
      'ValueType'   = Parse-PolFile -Path $regPolPath | Select-Object -Property ValueType
      'ValueLength' = Parse-PolFile -Path $regPolPath | Select-Object -Property ValueLength
      'ValueData'   = Parse-PolFile -Path $regPolPath | Select-Object -Property ValueData
  }
    #$regKeyInfo = Parse-PolFile -Path $regPolPath | Select-Object -Property DisplayName,KeyName,ValueName,ValueType,ValueLength,ValueData
    write-output $Object
  }
}
$Object | Export-Csv -Path 'c:\ADBackup\GPOSettings.csv' -NoTypeInformation -Delimiter ','
Get-Content -Path 'c:\ADBackup\GPOSettings.csv'

Output is not correct and only shows the last GPO processed:

“DisplayName”,“KeyName”,“ValueName”,“ValueType”,“ValueLength”,“ValueData”
“WindowsUpdates”,“System.Object”,“System.Object”,“System.Object”,“System.Object”,“System.Object

I cannot test at the moment but something like this should work actually

Install-Module -Name GPRegistryPolicy -force
$domainController = 'DC01'
$domainName = 'mycorp.com'

$GPOList = Get-GPO -all | Sort-Object -Property DisplayName

$Result =
foreach ($DisplayName in $GPOList) {
    $GPOName = $DisplayName.DisplayName
    $gpoGuid = (Get-GPO -Name $GPOName).Id.ToString()
    $registryPolPath = "\\$domainController\sysvol\$domainName\Policies\{$gpoGuid}\Machine"
    $RegPolPath = Join-Path -Path $registryPolPath -ChildPath 'registry.pol'
    if (Test-Path -Path $RegPolPath) {
        $ParsedProfile = Parse-PolFile -Path $regPolPath
        [PSCustomObject]@{
            'DisplayName' = $GPOName
            'KeyName'     = ($ParsedProfile).KeyName
            'ValueName'   = ($ParsedProfile).ValueName
            'ValueType'   = ($ParsedProfile).ValueType
            'ValueLength' = ($ParsedProfile).ValueLength
            'ValueData'   = ($ParsedProfile).ValueData
        }
    }
}
$Result |
    Export-Csv -Path 'c:\ADBackup\GPOSettings.csv' -NoTypeInformation -Delimiter ','

Not quite getting the values:

“DisplayName”,“KeyName”,“ValueName”,“ValueType”,“ValueLength”,“ValueData”
“Default Domain Policy”,“System.Object”,“System.Object”,“System.Object”,“System.Object”,“System.Object
“DMG_FSLogix”,“System.Object”,“System.Object”,“System.Object”,“System.Object”,“System.Object
“FirewallPortsPowerSettings”,“System.Object”,“System.Object”,“System.Object”,“System.Object”,“System.Object
“HLF_ChromeSettings”,“System.Object”,“System.Object”,“System.Object”,“System.Object”,“System.Object
“HLF_RDSLicensing”,“System.Object”,“System.Object”,“System.Object”,“System.Object”,“System.Object
“NTP Primary Domain Controller”,“System.Object”,“System.Object”,“System.Object”,“System.Object”,“System.Object
“PHC_RDSLicensing”,“System.Object”,“System.Object”,“System.Object”,“System.Object”,“System.Object
“SophosSSL”,“System.Object”,“System.Object”,“System.Object”,“System.Object”,“System.Object
“VDI_Settings”,“Software\Microsoft\Windows\CurrentVersion\Policies\System”,“HideFastUserSwitching”,“REG_DWORD”,“4”,“1”
“WindowsUpdates”,“System.Object”,“System.Object”,“System.Object”,“System.Object”,“System.Object

Please, when you post error messages format them as code.

Looks like the variable $ParsedProfile is an array instead of a single object. So I assume your custom function Parse-PolFile returns arrays instead of single objects. Without seeing your actual code it’s just a wild guess but you may try it anyway:

$domainController = 'DC01'
$domainName = 'mycorp.com'

$GPOList = Get-GPO -all | Sort-Object -Property DisplayName

$Result =
    foreach ($GPO in $GPOList) {
        $registryPolPath = "\\{0}\sysvol\{1}\Policies\{2}\Machine" -f $domainController, $domainName, $($GPO.DisplayName).id.GUID
        $RegPolPath = Join-Path -Path $registryPolPath -ChildPath 'registry.pol'
        if (Test-Path -Path $RegPolPath) {
            $ParsedProfileList = Parse-PolFile -Path $regPolPath
            foreach ($ParsedProfile in $ParsedProfileList) {
                [PSCustomObject]@{
                    'DisplayName' = $GPO.DisplayName
                    'KeyName'     = ($ParsedProfile).KeyName
                    'ValueName'   = ($ParsedProfile).ValueName
                    'ValueType'   = ($ParsedProfile).ValueType
                    'ValueLength' = ($ParsedProfile).ValueLength
                    'ValueData'   = ($ParsedProfile).ValueData
                }
            }
        }
    }

$Result |
    Export-Csv -Path 'c:\ADBackup\GPOSettings.csv' -NoTypeInformation -Delimiter ','
$GPOName = "PHC_RDSLicensing"
$gpoGuid = (Get-GPO -Name $GPOName).Id.ToString()
$registryPolPath = "\\$domainController\sysvol\$domainName\Policies\{$gpoGuid}\Machine"
Get-ChildItem -Path $registryPolPath
$RegPolPath = Join-Path -Path $registryPolPath -ChildPath 'registry.pol'
#Parse-PolFile -Path $regPolPath
#Capture the registry key path
$regKeyInfo = Parse-PolFile -Path $regPolPath | Select-Object -Property DisplayName,KeyName,ValueName,ValueType,ValueLength,ValueData
$regKeyInfo | Export-Csv -Path 'c:\ADBackup\GPOSettings.csv' -NoTypeInformation -Delimiter ','
Get-Content -Path 'c:\ADBackup\GPOSettings.csv'

Output:
“DisplayName”,“KeyName”,“ValueName”,“ValueType”,“ValueLength”,“ValueData”
,“SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services”,“LicensingMode”,“REG_DWORD”,“4”,“4”
,“SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services”,“LicenseServers”,“REG_SZ”,“38”,“rds.mycorp.com

I’m sick of it. Is it really that hard to format this stuff as code?

And how about a comment? What do you expect us to do now with your last post?

2 Likes
#Initial Setup Feb 21, 2022
Install-Module -Name GPRegistryPolicy -force
$domainName = $Env:USERDNSDOMAIN
$domainController = $env:LOGONSERVER -replace '\\',''

$GPOList = Get-GPO -all | Sort-Object -Property DisplayName

$Result =
    foreach ($GPO in $GPOList) {
        $GPOName = $GPO.DisplayName
        $gpoGuid = (Get-GPO -Name $GPOName).Id.ToString()
        $registryPolPath = "\\$domainController\sysvol\$domainName\Policies\{$gpoGuid}\Machine"
        $RegPolPath = Join-Path -Path $registryPolPath -ChildPath 'registry.pol'
        if (Test-Path -Path $RegPolPath) {
            $ParsedProfileList = Parse-PolFile -Path $regPolPath
            foreach ($ParsedProfile in $ParsedProfileList) {
                [PSCustomObject]@{
                    'DisplayName' = $GPOName
                    'KeyName'     = ($ParsedProfile).KeyName
                    'ValueName'   = ($ParsedProfile).ValueName
                    'ValueType'   = ($ParsedProfile).ValueType
                    'ValueLength' = ($ParsedProfile).ValueLength
                    'ValueData'   = ($ParsedProfile).ValueData
                }
            }
        }
    }

if (!(Test-Path -Path "C:\ADBackup")) {New-Item -ItemType directory -Path "C:\ADBackup" | Out-Null}
$Result |
    Export-Csv -Path 'C:\ADBackup\GPOSettings.csv' -NoTypeInformation -Delimiter ','
Get-Content -Path 'C:\ADBackup\GPOSettings.csv'

I really appreciate your help and got it working exactly the way I want. Sharing the final copy with the community In hopes it helps someone else. Sorry about the formatting. I grew up way too long ago writing code on a notepad with a pencil and not having all this fancy formatting and color options modern day editors have.