How to automate your custom packages

Brock Bingham candid headshot
Brock Bingham|Updated June 1, 2023

So you finally finished automating all of your standard applications using PDQ Deploy, and you’re ready for a one-way trip to Relaxationville when out of the blue, Nick from accounting asks to have Discord updated. After removing Nick from your Christmas card list, you reluctantly manually update Discord while thinking to yourself, “It sure would be nice to automate packages that aren’t in the Package Library.” Well, we’ve heard you loud and clear (I can neither confirm nor deny that PDQ has the ability to read your thoughts).


PDQ Deploy has access to hundreds of pre-built packages in its package library. These packages include a wide range of the most commonly used applications. Occasionally, you may need to support an application that isn’t included in the package library. Manually building custom packages in Deploy is simple and straightforward; automating custom packages, on the other hand, takes a little more work and know-how. If you’re determined enough to be a truly lazy sysadmin, though, the effort is worth the payoff.

Setting up custom variables

The first thing we need to do is to create some new variables in PDQ Deploy and Inventory. Custom variables will allow us to make versioning comparisons and build dynamic collections. Variables may take a little more work setting up initially, but they save us time and effort in the long run, which we at PDQ are all about. As our custom package gets updated, we’ll use PowerShell to edit the custom variable information automatically.

To create our custom variables, we first need to get the initial version number that will be assigned to the variable. Launch Discord and click on User settings. Scroll down, and just below “Log Out,” you will find the current version number.

Log Out

Now that we have our current version number, we can set up our custom variables in PDQ Deploy:

  1. With PDQ Deploy open, click Options > Variables

  2. Click New Variable

  3. For the name, enter DiscordName. Deploy should automatically update the variable to @(DiscordName)

  4. For the value, enter Discord

  5. Now we’ll create a variable for the version. Click New Variable

  6. For the name, enter DiscordVer. Again this should automatically update to @(DiscordVer)

  7. For the value, enter the version number we identified from the previous step. As of this post, the current version is 0.0.309

  8. Your variables should look like this, though your version number should be whatever the current version is.

Variables Locked

Next, create the same variables in PDQ Inventory following the same steps as above.

Creating a custom Discord package

Now that we have our variables, we’ll create our custom Discord package in Deploy.

  1. Download the Discord install file if you don’t already have it

  2. Save the install file to your Deploy repository (you can view your repository location by clicking on Options > Preferences > Repository). In this example, I’ve saved the Discord example to C:\Users\Public\Documents\Admin Arsenal\PDQ Deploy\Repository\Discord\0.0.309\DiscordSetup.exe

  3. In PDQ Deploy, click New Package

  4. Enter Discord for the name of the package

  5. Click Steps > Install

  6. Enter the file path in the Install File field. My file path is $(Repository)\@(DiscordName)\@(DiscordVer)\DiscordInstall.exe

  7. Add the silent parameter -s

  8. Click Save

Here’s a screenshot of the finished package.

Discord Package

Creating dynamic collections in PDQ Inventory

Dynamic collections allow us to filter computers based on evolving information. For this example, we’ll use dynamic collections to filter based on which computers have the newest version of Discord, an old version of Discord, and which computers don’t have Discord installed.

  1. In PDQ Inventory, click New Dynamic Collection

  2. Enter Discord for the Collection Name

  3. Configure this as your filter Application > Name > Equals >

  4. Use the custom variable button to insert your custom variable @(DiscordName) in the value column

  5. Click OK

    Discord Dynamic
  6. Now we will need to create three new dynamic collections nested under this dynamic collection. Right-click on the Discord collection and click New > Dynamic Collection

  7. Match these settings for the three nested collections

Discord Latest
Discord Not Installed
Discord Old

Creating a deployment schedule

In order to get this package to push out updates regularly, we need to configure a deployment schedule in PDQ Deploy. Since each network environment is unique, there is no perfect way to configure a deployment schedule. The best approach is to discuss it with your team and decide on a schedule that is effective while limiting the impact it has on your users and bandwidth.

  1. Launch PDQ Deploy

  2. Click on New Schedule

  3. Enter a schedule name

  4. Click on the Triggers tab

  5. I’ve configured my trigger to kick off weekly at 4:00 PM

    Discord Schedule
  6. Click on the Targets tab

  7. Click Choose Targets > PDQ Inventory > Collection

  8. Select the Discord (Old) collection and click OK

  9. Click the Packages tab

  10. If you had Discord selected when you clicked New Schedule, it should have added the Discord package automatically. If Discord wasn’t selected, click Attach Packages, then click on Discord and click > then click OK

  11. Click on the Options tab

  12. Make sure Stop deploying to targets once they succeed is selected

  13. Click OK to finish the schedule

Once the schedule has been created, we get the schedule ID number. Click All Schedules and make a note of the Discord schedule ID number.

Ad Schedules

Using PowerShell to download updates

Our local PowerShell expert has been hard at work whipping up a script that will go out to the web and retrieve the latest version of Discord and download it to the PDQ Deploy repository. This script can be used for other applications; however, it will take some customizing on your part to identify the web source as well as the best way to extract the version numbers.

The script:
#Database Locations $PDQDepDB = "C:\ProgramData\Admin Arsenal\PDQ Deploy\Database.db" $PDQInvDB = "C:\ProgramData\Admin Arsenal\PDQ Inventory\Database.db" #Get the Current Version $oldversion = sqlite3.exe $PDQInvDB "Select Value from CustomVariables where Name = 'discord';" #Repo for this software $Repository = "C:\Users\Public\Documents\Admin Arsenal\PDQ Deploy\Repository\Discord" #Download file name $file = "DiscordSetup.exe" #Get Version Number $HTTPFolderUrl = "" $HTTPRequest = [System.Net.HttpWebRequest]::Create("$HTTPFolderUrl") $HTTPRequest.Method = [System.Net.WebRequestMethods+Http]::Head $HTTPResponse = $HTTPRequest.GetResponse() $NewVersion = $HTTPResponse.ResponseUri.localpath.Split("/")[6] #If different then download and build folder if($newversion -ne $oldversion){ New-Item -Path $($Repository + "\" + $newversion) -ItemType Directory Invoke-WebRequest $HTTPFolderUrl -OutFile $($Repository + '\' + $newversion + '\' + $file)}

If there is a newer version of Discord available, this script creates a WebRequest to the Discord servers, downloads the current version, and identifies the current version number. Then it creates a new folder with the version number as the name within the Deploy repository.

Using PowerShell to update variables

Now that we have our PowerShell script downloading the newest version of Discord and putting it into the repository, we need another PowerShell script to update our variables and clean up our repository folder.

The script:
###Define Variables $autopath = "C:\Users\Public\Documents\Admin Arsenal\PDQ Deploy\Repository" $autopackages = @() $DBPath = "C:\programdata\Admin Arsenal\PDQ Deploy\Database.db" ###Build Package Objects $Discord = New-Object psobject -Property @{ Path = "\Discord" ScheduleID = "5" CustomVariable = "DiscordVer" } #$App2 = New-Object psobject -Property @{ # Path = "\App2" # ScheduleID = "2" # CustomVariable = "App2" #} #$App3 = New-Object psobject -Property @{ # Path = "\App3" # ScheduleID = "3" # CustomVariable = "App3" #} ###Populate Array #$autopackages += $Discord, $App2, $App3 $autopackages += $Discord ###To core of That Thing We Are Doing Foreach($Package in $autopackages){ ###Check to see if there is a new Package to Update If((Get-ChildItem -Directory -Path ($autopath + $Package.path)).count -eq "3"){ ###Move The old crap to the Audit Folder and Update the Variable in Inventory and Deploy $archive = Get-ChildItem -Directory -Path ($autopath + $Package.path) | Where{$ -eq "archive"} | select fullname Get-ChildItem -Directory -Path ($autopath + $Package.path) | Where{$ -ne "archive"} | sort creationtime | select -First 1 | Move-Item -Destination $archive.FullName $NewerVersion = Get-ChildItem -Directory -Path ($autopath + $Package.path) | Where{$ -ne "archive"} & pdqdeploy updatecustomvariable -name $Package.CustomVariable -value $NewerVersion.Name & pdqinventory updatecustomvariable -name $Package.CustomVariable -value $NewerVersion.Name ###Delete Schedule History $SQLQuery = "Select Name FROM ScheduleComputers WHERE ScheduleId LIKE $($Package.ScheduleID)" $ScheduleHistory = $SQLQuery | sqlite3.exe $DBPath foreach($machine in $ScheduleHistory){ & pdqdeploy DeleteScheduleHistory -Computer $machine -Schedule $Package.ScheduleID } } }

As you can see, there are additional applications listed in this script besides just Discord. Normally I would remove these other apps, but this script can handle multiple applications at a time, not just one, so I kept them in the script so you would know how to add more applications to it in the future.

Here are a couple of key takeaways about this script. Remember when I told you to get the schedule ID after you created the schedule? You need to add that schedule ID to the ScheduleID property. Also, you need to have your folders configured correctly. Inside the repository application folder, you need to have it configured with a current version folder and an archive folder, like this.

Discord Archive

When the script runs, it will check to see if the Discord folder has three folders inside of it. If there are only two folders, the script will skip the folder. When a new Discord update is available and you run the download Discord script, a third folder will be added to the Discord folder with the latest version. With three folders in there, the script will move the old version folder into the archive folder and update the DiscordVer variable in Deploy to the newest version number. Note that if you have the variable window open in Deploy at the time this script runs, the script will not be able to update the variable.

Wrapping up

You now have all the tools to automate your custom packages. The only thing left to do is to configure Task Scheduler to kick off these scripts on a schedule or schedule them to run with PDQ Deploy. If you decide to use PDQ Deploy to run these scripts, have it run locally and make sure to remove the Stop deploying to targets once they succeed setting from the Options tab in the scheduler. I would recommend having these scripts run prior to your scheduled deployment. Make sure to have the download script run first, then have the variable script run. Depending on the size of the application that you are downloading, you may need to give the download script plenty of time to finish running.

Now, with the ability to automate your custom packages, you don’t have to worry about Nick wrecking your precious trip to Relaxationville. He’ll have the latest version of Discord, and you won’t have to lift a finger to do it. Feel free to keep him off your Holiday card list, though; he earned that one.

Brock Bingham candid headshot
Brock Bingham

Born in the '80s and raised by his NES, Brock quickly fell in love with everything tech. With over 15 years of IT experience, Brock now enjoys the life of luxury as a renowned tech blogger and receiver of many Dundie Awards. In his free time, Brock enjoys adventuring with his wife, kids, and dogs, while dreaming of retirement.

Related articles