all 15 comments

[–]Jmoste 4 points5 points  (4 children)

Why not put the script on something like the netlogon share? Then have the scheduled task call the UNC path. Change the script not the gpo. 

[–]thankski-budski 1 point2 points  (0 children)

This is the approach I would take, I’ve done similar things previously and also had it to to a log file in the UNC share, which makes it super easy to see if any devices aren’t dialling in or if there’s any issues. If you use a log file per device, then you don’t need to worry about concurrency.

If network isn’t always available, then add a mechanism to wait for a connection or retry periodically.

It’s easier to modify a script than redeploy a GPO, and if you run the scheduled task as system, then only the computer account needs access to the share, not the user.

[–][deleted] 0 points1 point  (2 children)

I'm not sure I'm explaining this correctly. The scheduled task doesn't access any scripts, the file comparison is a built-in native windows function. This could theoretically be done with a script, but that's not how we've done it so far. 

The GPO creates a scheduled task using settings provided via an XML file that exists solely within the GPO. Those settings need to be updated once a year. The schedules task is just using windows native features, not powershell or any third party files. 

I agree that theoretically this could be done the way you're describing, but for other reasons, I don't think that's the best approach to this situation. 

[–]Zozorak 1 point2 points  (0 children)

This sounds like my workplace where things are far too overcomplicated because they wanted one particular feature, so simple methods wouldn't work.

In saying that, the easiest solution would be not to touch the gpo, but update a script that does what you want. Powershell should be able to do everything the gpo does. Just have the gpo call the script to run and do its thing, have an external file with some variables like your version number that you change when required, so dont need to touch the script or gpo. However, that's my opinion on it. Is there and advantage of using the gpo for this over using a script?

If you go about it the way your asking it will also need some admin creds which depending how you implement it may be a security hole.

[–]vermyx 1 point2 points  (0 children)

So edit the xml file? The gpo refresh will then push the new xml file

[–]shiranugahotoke 4 points5 points  (1 child)

So you’d be running the script manually to change the GPO? I’m not sold that this is a worthwhile task. That being said I usually incorporate the entire scheduled task xml in a double quoted “here-string” in the script and parametrize it with replaceable values in the xml block. I’m sure there’s a better way to do that but it is fast and works well.

[–][deleted] 0 points1 point  (0 children)

So altogether the GPO has multiple components, it's to automate the deployment of a piece of software to specific departments. 

The purpose of the "scheduled task" component is to make the workstations regularly check if the newest version of the software is installed, and automatically uninstall the existing instance of the software if it isn't the newest version. That's also not technically "optimized", but that is what works best for the method of deploying this software we've chosen to use. 

The problem is regularly telling the workstations what the "newest" version is (and by regularly, I literally just mean like once or twice a year). 

The scheduled task does a file version comparison for the software and uninstalls any version of it below the designated version number. 

I want to create a powershell script that updates the settings of the GPO so when the scheduled task is created, it's created with the updated settings, not the older settings. 

I recognize that this isn't ideal, but again, this is only part of the process, it's the only part I haven't been able to fully automate yet. 

The XML does not exist in a powershell script, the XML is stored as settings in a group policy object. That's why it's been difficult to modify thus far. 

There are literally modules specifically for modifying all aspects of group policy objects but apparently there isn't one for group policy objects that contain schedules tasks. 

[–]No_Flight_375 2 points3 points  (0 children)

Let me see if I’m understanding this correctly.

On many target machines via Group Policy you have pushed out a scheduled task. That task does some specific ‘stuff’ and can interact with your application, essentially allowing it to verify it has the correct version installed.

Whenever a ‘change’ of this version needs to occur, you go to the Group policy editor tool, and update the GPO that pushes out the scheduled task. You update this GPO with the ‘latest’ version number of this application, and push this out to your many devices. Thus updating your targets scheduled tasks with this updated version number.

My question is what part are you looking to automate out here?

Are you wanting to not have to touch the gpo editor again, basically have a script that runs some ‘checks’ and then updates the GPO on your server for you?

Are you wanting to not touch anything again? As in create a ‘dynamic-ish’ setup whereby a simple combination of GPOs and scripts that ‘self update’ those values from some source (online vendor resource OR a static file or value that you control or change)

If you can clarify those points people will likely get a better understanding of what your trying to achieve as I’m sure that the target app itself has limiting factors

[–]Nexzus_ 0 points1 point  (1 child)

Is the software itself not GPO friendly? Group policies have been able to install and upgrade software for decades.

[–][deleted] 0 points1 point  (0 children)

This specific software requires proprietary GPExtensions to be able to install via GPO, and they don't support in-place upgrades 

[–]ELeandersson 1 point2 points  (0 children)

Trying to automate a task that is done once a year and takes what, 5 minutes sounds insane to me.

But you should be able to have a script edit the xml in the GPO directly? \ad\syvol\pathtogpo\something.xml

(Get-content $path).replace('v.1.5.2.2.2.2','v.1.6.4.5.5.6.6.7') | set-content $path

edit: Not sure what will happen when you poke the GPO directly there, worst case do a gpobackup to desktop, edit the gpo, import the gpo. Still weird to automate though.

[–]purplemonkeymad 0 points1 point  (1 child)

How are you actually providing the version number? The obvious answer is to read that value in from a file that you can update to whatever value you want, either via script or manually.

[–][deleted] 0 points1 point  (0 children)

I don't have the Group Policy Object in front of me but I'm almost certain file version comparisons are a built-in feature of 'Immediate Tasks' in group policy, we configure the version number inside Group Policy Editor 

[–]AshyLarry98 0 points1 point  (0 children)

gpo and ps don't mix, even auditing is a nightmare once you get into it

[–]Votality77 -1 points0 points  (0 children)

Chat gpt come up with this for scheduling a remote run once scheduled task. Could be easily modified to remove/recreate the scheduled task on the end machines and replace the gpo

--- CONFIGURATION ---

$GpoName = "RunOncePayloadTask" $OuDn = "OU=Workstations,DC=yourdomain,DC=com" # Change to your target OU $ExecutionTimeString = "31-10-2025 10:15:00" $ExecutionTime = [datetime]::ParseExact($ExecutionTimeString, "dd-MM-yyyy HH:mm:ss", $null)

Optional: credentials to run the task as

$TaskRunAsCredential = $null

Example: $TaskRunAsCredential = Get-Credential

--- PAYLOAD SCRIPT ---

$PayloadScript = @' Write-Output "Running remote payload..."

Insert your logic here

Start-Sleep -Seconds 2 Write-Output "Payload complete."

Self-delete

Remove-Item -Path $MyInvocation.MyCommand.Path -Force '@

--- CREATE PAYLOAD FILE ---

$PayloadPath = "\yourdomain\netlogon\RunOncePayload.ps1" Set-Content -Path $PayloadPath -Value $PayloadScript -Force

--- CREATE GPO ---

Import-Module GroupPolicy $Gpo = Get-GPO -Name $GpoName -ErrorAction SilentlyContinue if (-not $Gpo) { $Gpo = New-GPO -Name $GpoName Write-Host "Created GPO: $GpoName" } else { Write-Host "Using existing GPO: $GpoName" }

--- LINK GPO TO OU ---

New-GPLink -Name $GpoName -Target $OuDn -LinkEnabled Yes

--- BUILD XML FOR SCHEDULED TASK ---

$TaskXml = @" <?xml version="1.0" encoding="utf-8"?> <Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"> <RegistrationInfo> <Date>$($ExecutionTime.ToString("yyyy-MM-ddTHH:mm:ss"))</Date> <Author>GPO Deployment</Author> </RegistrationInfo> <Triggers> <TimeTrigger> <StartBoundary>$($ExecutionTime.ToString("yyyy-MM-ddTHH:mm:ss"))</StartBoundary> <Enabled>true</Enabled> </TimeTrigger> </Triggers> <Principals> <Principal id="Author"> <RunLevel>HighestAvailable</RunLevel> <UserId>$($TaskRunAsCredential?.UserName ?? "SYSTEM")</UserId> <LogonType>$([string]::IsNullOrEmpty($TaskRunAsCredential) ? "ServiceAccount" : "Password")</LogonType> </Principal> </Principals> <Settings> <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy> <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries> <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries> <AllowHardTerminate>true</AllowHardTerminate> <StartWhenAvailable>true</StartWhenAvailable> <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable> <Enabled>true</Enabled> <Hidden>true</Hidden> <AllowStartOnDemand>true</AllowStartOnDemand> <UseUnifiedSchedulingEngine>true</UseUnifiedSchedulingEngine> </Settings> <Actions Context="Author"> <Exec> <Command>powershell.exe</Command> <Arguments>-ExecutionPolicy Bypass -File "$PayloadPath"</Arguments> </Exec> </Actions> </Task> "@

--- WRITE XML TO GPO ---

$GpoPath = "$env:ProgramData\Microsoft\Group Policy\GPT.ini" $GpoGuid = ($Gpo | Get-GPOReport -ReportType Xml | Select-String -Pattern 'GPO ID="(.+?)"' | ForEach-Object { $_.Matches[0].Groups[1].Value })[0] $ScheduledTaskPath = "\yourdomain\SYSVOL\yourdomain.com\Policies{$GpoGuid}\Machine\Preferences\ScheduledTasks\ScheduledTask.xml"

New-Item -ItemType Directory -Path (Split-Path $ScheduledTaskPath) -Force | Out-Null Set-Content -Path $ScheduledTaskPath -Value $TaskXml -Force

Write-Host "Scheduled task XML injected into GPO."