Using PowerShell to generate and deploy Group Policies for non-domain environments

So I’ve been having a hard time coming up with a title for this one. As I’ve stated in previous blogs we’re moving more and more clients to cloud only environments using Azure AD, Teams, and Onedrive as their collaboration and file sharing solution. The issue with this was that it became quite difficult to deploy GPOs. This was especially bad when wanting to use ADMX templates. We could use Intune but found that even there we had so many limitation it would not work for us.

Full disclosure; There are some third party applications that offer GPO deployment. We found most of them to be overkill for what we needed, or not very suitable for the MSP market. I mean – We already have our RMM system, and tacking on another application just did not seem right to me.

So to solve the issues with GPO deployment for Azure AD environments, or workgroup environments I’ve created a PowerShell script that allows you to deploy group policies. I’ve also created a script to monitor if the deployment ran correctly. That way you can use your RMM to see who received the new policy and who has not.

Execution Script

Before we get started on the script, you will need the following two items: LGPO.exe, which we’ll use to export and deploy the policy, and winrar which will create our setup file. We download the LGPO.exe for you (Please host this somewhere you trust.). Winrar you’ll need to install yourself.

Disclaimer/warning: Please note that the script is destructive to the currently installed local group policies. Please run the script inside of a VM or machine that does not have any local policies. It will clear all policies by destroying the actual policy files. You have been warned 🙂

So the steps are straightforward – Save the script and execute it with the parameters you need. The script will delete the current GPOs, and then open the group policy editor. Import your ADMX files, edit the settings, and close the editor. Then the script will finish up and put 2 files on your desktop – One to apply the policy, the other to remove the policy in case you no longer need it.

<#
.SYNOPSIS
  Creates an execuble that can apply and remove a local Group Policy Object
.DESCRIPTION
 Creates an execuble that can apply and remove a specific local policy. The executable is a self-extracting winrar archive. Winrar installation is required for processing.
 The execuble will work in "MERGE" mode, meaning that settings that are duplicate will be overwritten, other settings will not be touched.

 Parameters are not required, but optional. Script will fail if LGPO and Winrar are not present.

 WARNING: SCRIPT IS DESTRUCTIVE TO LOCAL GROUP POLICIES. DO NOT RUN ON PRODUCTION MACHINES. Use at own risk.
.PARAMETER DownloadURL
    Specificies where to download LGPO from if not installed.
.PARAMETER DownloadLocation
    Specificies where to download LGPO to if not installed. Defaults to C:\Temp\LGPO
.PARAMETER WorkingPath
    Specificies where to put temporary files. Defaults to C:\Temp\LGPO
.PARAMETER WinrarPath
    Path where winrar is found. Defaults to C:\Program Files\WinRAR\WinRAR.exe
.PARAMETER GPOName
    Decides part of the name of the file that will be placed in C:\ProgramData\
.PARAMETER GPOVersion
    Decides part of the name of the file that will be placed in C:\ProgramData\
.INPUTS
  none
.OUTPUTS
  executable generated and stored in user desktop location
.NOTES
  Version:        0.4
  Author:         Kelvin Tegelaar
  Creation Date:  02/2020
  Purpose/Change: Initial script. Beta.

#>
Param(
    [string]$DownloadURL = "http://cyberdrain.com/wp-content/uploads/2020/02/LGPO.exe",
    [string]$DownloadLocation = "C:\Temp\LGPO",
    [string]$WinrarPath = "C:\Program Files\WinRAR\WinRAR.exe",
    [string]$GPOName = "GPO",
    [string]$GPOVersion = "1.0"
)

write-host "Checking if base folder exists in $DownloadLocation and if not, creating it." -ForegroundColor Green
try {
    $TestDownloadLocation = Test-Path $DownloadLocation
    if (!$TestDownloadLocation) {
        write-host "Creating Folder to download LGPO." -ForegroundColor Green
        new-item $DownloadLocation -ItemType Directory -force
    }
    $TestDownloadLocationExe = Test-Path "$DownloadLocation\LGPO.exe"
    if (!$TestDownloadLocationExe) { 
        write-host "Download LGPO." -ForegroundColor Green
        Invoke-WebRequest -UseBasicParsing -Uri $DownloadURL -OutFile "$DownloadLocation\LGPO.exe" 
    }
    $TestDownloadLocationBat = Test-Path "$DownloadLocation\LGPOExecute.bat"
    if (!$TestDownloadLocationBat) { 
        write-host "Generating configuration batch file." -ForegroundColor Green

        @"
LGPO.exe /t ComputerPolicy.txt /v > "C:\ProgramData\$GPOName $GPOVersion Computer.log"
LGPO.exe /t UserPolicy.txt /v > "C:\ProgramData\$GPOName $GPOVersion User.log"
"@ | Out-File -Encoding ascii "$DownloadLocation\LGPOExecute.bat" -Force
    
    }
}
catch {
    write-host "The download and extraction of LGPO.EXE failed. Error: $($_.Exception.Message)" -ForegroundColor Red
    exit 1
}
write-host "Clearing all existing policies." -ForegroundColor Green
#Clears all local policies
remove-item -Recurse -Path "$($ENV:windir)\System32\GroupPolicyUsers" -Force -erroraction silentlycontinue
remove-item -Recurse -Path "$($ENV:windir)\System32\GroupPolicy" -Force -erroraction silentlycontinue
remove-item -Recurse -Path "$DownloadLocation\ComputerPolicy.txt" -Force -erroraction silentlycontinue
remove-item -Recurse -Path "$DownloadLocation\Userpolicy.txt" -Force -erroraction silentlycontinue
write-host "Running GPUpdate to clear local policy cache." -ForegroundColor Green
gpupdate /force
write-host "Starting GPEdit. Please create your policy. After closing GPEdit we will resume." -ForegroundColor Green
start-process "gpedit.msc" -Wait
write-host "Exporting policies with LGPO." -ForegroundColor Green
& &quot;$DownloadLocation\LGPO.EXE" /parse /m "$($ENV:windir)\System32\GroupPolicy\Machine\Registry.pol" > $DownloadLocation\ComputerPolicy.txt
& &quot;$DownloadLocation\LGPO.EXE"  /parse /u "$($ENV:windir)\System32\GroupPolicy\User\Registry.pol" > $DownloadLocation\Userpolicy.txt
write-host "Sleeping for 10 seconds to give LGPO a chance to export all settings if GPO is large." -ForegroundColor Green
start-sleep 10
$UserDesktop = [Environment]::GetFolderPath("Desktop")
@"
Setup=LGPOExecute.bat
TempMode
Silent=1
"@ | out-file "$DownloadLocation\SFXConfig.conf" -Force

write-host "Creating Apply executable and placing on current user desktop." -ForegroundColor Green
& $WinrarPath -s a -ep1 -r -o+ -dh -ibck -sfx  -iadm -z&quot;C:\temp\LGPO\SFXConfig.conf" "$UserDesktop\$GPOName $GPOVersion Apply Policy.exe" "$DownloadLocation\*"
start-sleep 3

write-host "Creating Remove executable and placing on current user desktop." -ForegroundColor Green
$ComputerPolicy = get-content "$DownloadLocation\ComputerPolicy.txt"
$UserPolicy = get-content "$DownloadLocation\UserPolicy.txt"
$ReplacementArray = @("DELETEKEYS", "DELETE", "QWORD", "SZ", "EXSZ", "MULTISZ", "BINARY", "CREATEKEY", "DELETEALLVALUES", "DWORD")
foreach ($Replacement in $ReplacementArray) {
    $ComputerPolicy = $ComputerPolicy | Foreach-Object { $_ -replace "^.*$replacement.*$", "CLEAR" }
    $UserPolicy = $UserPolicy | Foreach-Object { $_ -replace "^.*$replacement.*$", "CLEAR" }
}
$UserPolicy | out-file "$DownloadLocation\Userpolicy.txt"
$ComputerPolicy | out-file "$DownloadLocation\ComputerPolicy.txt"

& $winrarPath -s a -ep1 -r -o+ -dh -ibck -sfx  -iadm -z"C:\temp\LGPO\SFXConfig.conf" "$UserDesktop\$GPOName $GPOVersion Remove Policy.exe" "$DownloadLocation\*"
remove-item -Recurse -Path "$DownloadLocation\LGPOExecute.bat" -Force -erroraction silentlycontinue

Deploy the files on your desktop as-if its an application via your RMM.

Monitoring the deployment

So you can run the executable and when it runs it will create 2 log files in C:\ProgramData, one for User policies, the other for Computer policies. To monitor these, you check if the file exists and if it contains “Policy saved” in the last 3 lines.

$GPOFile = "C:\ProgramData\GPO 1.0 User.log"

$Check = Test-Path "C:\ProgramData\GPO 1.0 User.log"
if (!$Check) { 
    $Healthstate = "GPO has not deployed. Log file does not exist."
}
else {
    $State = get-content $GPOFile | Select-Object -last 3
    if ($state[0] -ne "POLICY SAVED.") { $Healthstate = "GPO Log found but policy not saved." } else { $Healthstate = "Healthy" }
}

And that’s it! With this you’ll be able to deploy GPOs as if you still have your local AD domain, something we really missed in our cloud only deployments. As always, Happy PowerShelling.

3 thoughts on “Using PowerShell to generate and deploy Group Policies for non-domain environments

  1. Pingback: ICYMI: PowerShell Week of 06-March-2020 | PowerShell.org

  2. Pingback: PowerShell SnippetRace 10-2020 – PowerShell Usergroup Austria

  3. Pingback: Documenting and monitoring blogs updates - CyberDrain

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.