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 & "$DownloadLocation\LGPO.EXE" /parse /m "$($ENV:windir)\System32\GroupPolicy\Machine\Registry.pol" > $DownloadLocation\ComputerPolicy.txt & "$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"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.
Pingback: ICYMI: PowerShell Week of 06-March-2020 | PowerShell.org
Pingback: PowerShell SnippetRace 10-2020 – PowerShell Usergroup Austria
Pingback: Documenting and monitoring blogs updates - CyberDrain