Last active
July 9, 2026 17:02
-
-
Save lollilol/b9214ced8a91267587d57b25c263414d to your computer and use it in GitHub Desktop.
deploy packages using a gpo startup script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # pkgdeploy | |
| # Copyright (C) 2026 Jascha Johnsen | |
| # | |
| # This program is free software: you can redistribute it and/or modify | |
| # it under the terms of the GNU Affero General Public License as published by | |
| # the Free Software Foundation, either version 3 of the License, or | |
| # (at your option) any later version. | |
| # | |
| # This program is distributed in the hope that it will be useful, | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| # GNU Affero General Public License for more details. | |
| # | |
| # You should have received a copy of the GNU Affero General Public License | |
| # along with this program. If not, see <https://www.gnu.org/licenses/>. | |
| # defining variables | |
| $scriptName = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) | |
| $LogDir = "C:\temp\company" | |
| $LogFile = "$LogDir\$scriptName-$(Get-Date -Format 'yyyy-MM-dd').log" | |
| # create log directory | |
| mkdir -ErrorAction SilentlyContinue $LogDir | |
| # clean logs older than 30 days | |
| Get-ChildItem "$LogDir\$scriptName-*.log" | Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-30)} | Remove-Item | |
| # can be removed later | |
| rm -ErrorAction SilentlyContinue C:\temp\company\domain_wide_at_startup.log | |
| rm -ErrorAction SilentlyContinue C:\temp\company\domain_wide_at_startup*.log | |
| # make winget work when running as SYSTEM | |
| $wingetpath = (Resolve-Path "C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_*_x64__8wekyb3d8bbwe" | Sort-Object -Property Path | Select-Object -Last 1) | |
| $winget_folder = $wingetpath.Path | |
| $vclibspath = (Resolve-Path "C:\Program Files\WindowsApps\Microsoft.VCLibs*UWPDesktop*_x64__8wekyb3d8bbwe" | Sort-Object -Property Path | Select-Object -Last 1) | |
| $vclibs_folder = $vclibspath.Path | |
| $env:PATH = "$env:PATH;$winget_folder;$vclibs_folder" | |
| # defining functions | |
| # example | |
| # Write-Log "This is an error" "ERROR" | |
| # Write-Log "This is an info" | |
| # example 2 | |
| # try { | |
| # some command | |
| # Write-Log "Successful" "INFO" | |
| # } catch { | |
| # Write-Log "ERROR: $($_.Exception.Message)" "ERROR" | |
| # } | |
| function Write-Log { | |
| param([string]$Message, [string]$Level = "INFO") | |
| $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" | |
| "$timestamp [$Level] $Message" | Out-File -FilePath $LogFile -Append -Encoding UTF8 | |
| } | |
| function Install-Application { | |
| param( | |
| [Parameter(Mandatory=$true)] | |
| [ValidateSet("LocalFile", "Winget")] | |
| [string]$InstallType, | |
| [Parameter(Mandatory=$true)] | |
| [string]$AppName, | |
| [string]$AppId, | |
| [string]$ExecutablePath, | |
| [string]$Arguments, | |
| [string]$GroupName | |
| ) | |
| $computerName = $env:COMPUTERNAME | |
| $shouldInstall = $true | |
| # Check group membership if GroupName is specified | |
| if ($GroupName) { | |
| $shouldInstall = $false | |
| try { | |
| $root = [ADSI]"LDAP://DC=company,DC=local" | |
| $searcher = New-Object System.DirectoryServices.DirectorySearcher($root) | |
| $searcher.Filter = "(&(objectClass=computer)(cn=$computerName))" | |
| $result = $searcher.FindOne() | |
| if ($result) { | |
| $computerDN = $result.Properties["distinguishedname"][0] | |
| $group = [ADSI]"LDAP://CN=$GroupName,OU=Software,OU=Berechtigungen,OU=Gruppen,OU=stuff,DC=company,DC=local" | |
| $group.RefreshCache() | |
| $shouldInstall = $group.Properties["member"] -contains $computerDN | |
| } | |
| } catch { | |
| Write-Log "ERROR: $($_.Exception.Message)" "ERROR" | |
| return | |
| } | |
| } | |
| # Install if applicable | |
| if ($shouldInstall) { | |
| try { | |
| if ($InstallType -eq "LocalFile") { | |
| Start-Process -FilePath $ExecutablePath -ArgumentList $Arguments -NoNewWindow | |
| } else { | |
| & winget install --accept-source-agreements --accept-package-agreements -e --id $AppId | |
| } | |
| } catch { | |
| Write-Log "ERROR: $($_.Exception.Message)" "ERROR" | |
| } | |
| } | |
| } | |
| ## Usage | |
| # If -GroupName is not specified, it will be installed for ALL machines! | |
| # The group must be in OU=Software,OU=Berechtigungen,OU=Gruppen,OU=stuff,DC=company,DC=local | |
| # and contain machines, not users! | |
| ## Usage examples: | |
| # Brave for all: | |
| # Install-Application -InstallType LocalFile -AppName "Brave Browser" -ExecutablePath "\\s-fs01\Software\BraveBrowser\BraveBrowserSetup.exe" -Arguments "/silent", "/install" | |
| # Notepad++ for all: | |
| # Install-Application -InstallType Winget -AppName "Notepad++" -AppId "Notepad++.Notepad++" | |
| # Nextcloud Desktop restricted to the group "NextcloudDesktop": | |
| # Install-Application -InstallType Winget -AppName "Nextcloud" -AppId "Nextcloud.NextcloudDesktop" -GroupName "NextcloudDesktop" | |
| #### NO EDITS BEFORE THIS LINE | |
| ### all | |
| # Brave Browser | |
| Install-Application -InstallType LocalFile -AppName "Brave Browser" -ExecutablePath "\\s-fs01\Software\BraveBrowser\BraveBrowserSetup.exe" -Arguments "/silent /install" | |
| # Notepad++ | |
| Install-Application -InstallType Winget -AppName "Notepad++" -AppId "Notepad++.Notepad++" | |
| ### restricted to groups | |
| # Nextcloud Desktop | |
| Install-Application -GroupName "NextcloudDesktop" -InstallType Winget -AppName "Nextcloud" -AppId "Nextcloud.NextcloudDesktop" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment