Home » Create Dynamic Application Packages with PowerShell
Create Dynamic Application Packages with PowerShell
Published On: March 31, 2021Last Updated: January 5, 2024By David Brook15.1 min readViews: 525
TOC
Dynamic? In what way?
When I say the packages are dynamic, I mean that you don’t have to update the application package when a new version is released by the vendor.
There are caveats to both methods that we walk through below, there are also other community and paid for tools to do similar things.
However, the reason I wrote this post and started focusing on packaging applications in this was to avoid having support tickets when a new version is released, I also wanted to use this with ConfigMGR and Intune without the requirement of additional modules.
Show me the way!
Well lets show you a couple ways to do this, Web (HTML) Scraping and using the GitHub API.
Web Scraping
In my opinion, this is the most flawed method, as this replies on the website layout and/or table structure to stay the same as when you write your script. However, it is still an option and it works really well.
To be able to get the data from the tables in PowerShell we are going to need to use `Invoke-WebRequest`, Normally this would be super easy to use as it parses the HTML data for you. However, as this script will run as system in Intune you will need to launch it with `-UseBasicParsing` which complicates things a little more.
For this example we will use the Microsoft Remote Desktop Client, Are you ready? Lets begin. (You can achieve this using API Calls, However, this is a good example of table structure for Web Scraping)
Detection
Lets start by looking at the way we obtain the latest version and check it again the version in the registry.
As you can see from the image below, there is a version table right at the top of the web page.
If you press F12 and open the developer options, you can click through the HTML sections in the Elements tab and find the table like below;
As you can see from the snippet below we have to use a `HTMLContent` **COM** object to parse the HTML data so we can interact with the tables.
In it’s simplest form we get the RawContent and then write it to the IHTMLDocument2 object with the COM object, giving us the functionality  work with the tables.
Copy to Clipboard
Let’s take a closer look at the interaction with the tables, as you can see the variable $Tables uses the the $HTML variable which contains the COM object data to select everything with the tag of table (`$Tables = @($html.all.tags(‘table’))`). From this point it uses a for loop to gather the table data, until finally we decide which part of the table we want to use.
For example, We are focusing on the latest version, so if you run the for loop manually and look at $resultObjectin PowerShell it will return something like this;
From this point you can create a PSCustomObject with the table header you want. Now this is kind of over complicating it for this example as you could just return `$resultObject.’Latest version’` however, I use this loop for other methods and keeping it in this format helps me standardise the way I work, but it also gives you the ability to use if for other things too.
All of this is wrapped inside a function (`Get-LatestVersion`) as I plan on using the same script for the detection method as for the install, but I also like to re-check in my install script that the application definitely is not installed before the install action executes. If you look at the `Detect-Application` function you can see that I check both the 64-bit and 32-bit registry locations with an IFÂ statement based on the variables below;
Copy to Clipboard
The IF statement uses an `-or` operator, meaning if one of the conditions matches then run the code within the brackets below. As you can see from the variables the `$LatestVersion` uses the `Get-LatestVersion` function which is used to match the display version in the registry.
This the fundamental foundation of the operation, as we can now detect the application without using any additional modules in the next section we will look at the download and installation of the app.
Download Link
Now we know that we can Detect the application, lets look at obtaining the download link.
If you look at the below snippet, you can see we use a variable which calls a function to get the download link (`$DownloadLink = Get-DownloadLink`). For this to work there is a reliance on the the Variable `$Arch` been set, by default this is set to 64-bit. However, this is available as a command line parameter.
Copy to Clipboard
Lets take a look at the `Get-DownloadLink` function, the basics of getting the data and writing it to an HTMLCOM object is the same as the detection method, however this time we do not need to look at a table, we are specifically looking for a link which matched the `$Arch` variable.
Copy to Clipboard
If you look at the web page for the downloads you will see that the links are in an unordered list;
Again if you hit F12 and look at the html content behind the table, you will see the data we are looking for.
To get this using the script we simply run `($HTML.links | Where-Object {$_.InnerHTMl -Like “*$Arch*”}).href` simple right?
Download & Install
Before we look at the install function, lets look at the logic that calls the install.
Lets just assume you called the script with the Install execution type (`.`) or launched it without any parameters.
Lets look inside the default section (lines 17-27) below, firstly it will check if the latest version of the application is not installed using an IF statement, ELSEÂ return that it is already installed.
If the application is not installed it then proceeds to attempt the installation in a `try{} catch{}` statement. The basics of this is as it says, it will try the install, if it fails it will catch it and throw back the `Write-Error` text.
Copy to Clipboard
Lets take a look at the `Install-Application` function that is called in the statement.
Lets Break it down into stages.
Checks if the  `$DownloadPath` exists, if not it will try to create it.
Download the installer from the Link to the Download folder (`$DownloadPath`)
Install the MSI with the additional command line arguments `“$DownloadPath$InstallerName”” /qn /norestart /l* “”$DownloadPathRDINSTALL$(get-Date -format yyyy-MM-dd).log””`
Tip
When using double quotes (“) inside double quotes you must double them up.
For Example “The file is located: “”$VariablePath.txt”””
Copy to Clipboard
Uninstall
As we have a dynamic installation, we want the same for the uninstall right?
Well this is also achievable, take a look the the `Uninstall-Application` function below;
Copy to Clipboard
This is using some of the same logic as the **Detection** method, It checks both the 64-bit and the 32-bit registry keys to see if an application that is like the display name of our application.
If a registry entry is detected, it will obtain the Key Name in this case as we are dealing with an MSI. This is because the MSI Key name is the GUID, it will then build up the MSIEXEC arguments for the uninstall. After it has completed both steps it will then process with the uninstallation.
You will need to change the param block variable for `$ExecutionType` to `$ExecutionType = Detect` when using this as a detection method within Intune or ConfigMGR.
Copy to Clipboard
Your Content Goes That wraps up the Web Scraping method, I hope this proves useful when trying to make your apps more dynamic.
GitHub API
Using API calls is a better way to do dynamic updates. Some vendors host their content on GitHub as this provides build pipelines, wikis, projects and a whole host of other things. This is the method that is least likely to change, and if it does it will be documented using the GitHub API Docs.
For this example we are going to look at using Git for Windows, we will be using their GitHub Repo to query the version and also get the download.
Note
GitHub has a rate limit for the API calls, unautenticated calls has a rate limit of 60, GitHub authenticated accounts has a limit of 5000 and GitHub Enterprise accounts has a limit of 15000 calls.
Each time the script is launched it used1 call, so in terms of a detection and installation you will need a 2 api calls.
You will need to take this into account if you plan to package multiple applications in this way, you could use multiple accounts and randomise the PAC Key from an array, however this is something that should be highlighted.
If you look at line 30 above, you will notice that the versions matches the one on the latest release page.
Now we know what property within the API we are looking for and how it displays, we can head into PowerShell and start working on the detection.
First of all we need to get the latest version, to do this we first perform and API Call to get all of the information and store the information in the `$RestResult` variable.
Take a look at the below snippet;
Copy to Clipboard
The first thing to note on this snippet is the method it will use to connect to the API, If you specify a Personal Access Token with the `-GITPAC` parameter or via the variable in the script you will be able to have 5000 API calls for your application installs.
In short we specify the `$URL` variable and then run a GETÂ request with `Invoke-RestMethod` and specify that we want the output as `application/json`. Once it has the data we want to then format the `$LatestVersion` variable to return just the version number, for this we use the `.split()` operator, by default this splits on spaces, you can specify other characters to split it with by adding in something like `‘.’` and it would split the string at every point there is a dot. Now we have split the string, we want to select the index, for this example as the version number is at the end we want to select the index `[-1]`. If the index was at the start we would use `[0]`, feel free to experiment with this.
This variable is then used to call the `Detect-Application` function which will return `True` if the application is installed, otherwise it will return `null`.
Copy to Clipboard
Git Download Link
If you take a look back at the latest releases page, and scroll down to Assets, if you hover over one of them you will see the URL it links to in the bottom left-hand corner of your browser.
Now we know that we can Detect the application, lets look at obtaining the download link.
If you look at the script snippet below, you can see that we are still using the `$RestResult` to obtain the download link. To get the download link for the architecture you specify we first have to build up the `$EXEName` Variable, this uses the `$LatestVersion` and `$Arch` variables to bring the name together.
Once the name EXE Name is sorted, we then use this to get the link, by using the `Where-Object` function to select the download URL from the asset where the `$_.name` matches `$EXEName`.
Copy to Clipboard
Git Download & Install
The Install logic is the same as web scraping, however we will cover it here too so you don’t need to scroll up.
Lets just assume you called the script with the Install execution type (`.<ScriptName.ps1 -ExecutionType Install`) or launched it without any parameters.
Lets look inside the default section lines 15-27 below, firstly it will check if the latest version of the application is not installed using an IFÂ statement, ELSE return that it is already installed.
If the application is not installed it then proceeds to attempt the installation in a `try{} catch{}` statement. The basics of this is as it says, it will try the installation, if it fails it will catch it and throw back the `Write-Error` text.
Copy to Clipboard
Lets take a look at the `Install-Application` function that is called in the statement.
Lets Break it down into stages.
Checks if the  `$DownloadPath` exists, if not it will try to create it.
Download the installer from the Link to the Download folder (`$DownloadPath`)
Install the application with the additional command line arguments stored in the `$InstallArgs` variable.
Copy to Clipboard
Git Uninstall
As we have a dynamic installation, we want the same for the uninstall right?
Well this is also achievable, take a look the the `Uninstall-Application` function below;
Lets break this down,
Check if an application is installed with a display name like the string stored in `$DetectionString` (Checks both 64 and 32 Uninstall Keys)
If the application is installed, get the UninstallString from the key and store this in the `$UninstallEXE` variable.
Uninstall the application using the `$UninstallEXE` with the command line arguments stored in the `$UninstallArgs` variable.
Copy to Clipboard
Git Finished Script
If you compile all of the sections together with a little bit of formatting you will end up with a script like the one below.
You will need to change the param block variable for `$ExecutionType` to `$ExecutionType = Detect` when using this as a detection method within Intune or ConfigMGR.
Select App type Other>Windows app (Win32), Click Select
Click Select app package file, Click the Blue Folder icon to open the browse window
Select the .intunewin file you have created containing a copy of the script, Click Open and then click OK
Fill out the Name and Publisher mandatory fields, and any other fields you desire
Upload an icon if you desire, I would recommend doing this if you are deploying this to users via the Company Portal
Click Next
Enter your install command `powershell.exe -executionpolicy bypass “.<Script Name.ps1>”`
Enter your uninstall command `powershell.exe -executionpolicy bypass “.<Script Name.ps1>” -ExecutionType Uninstall`
Select your install behaviour as System
Select your desired restart behaviour, Adding custom return codes if required
Click Next
Complete your OS Requirements, At a minimum you need to specify the Architecture and the minimum OS Version (e.g. 1607/1703 etc.)
Click Next
For Detection rules, select Use a custom detection script
Script File: Browse to a copy of the Script where the ExecutionType was amended to`$ExecutionType = “Detect”`.
Assign the application to your desired group
Note
If you want to display the app in the company portal, it MUST be assigned to a group containing that user. Required Assignments will force the app to install, whereas Available will show this in the Company Portal. Click Next.
Click Create
ConfigMGR
Head over to your Software Library and Start Creating an application in your desired folder
General Tab, Select Manually Specify the application information
General Information – Input the information for your app
Software Center – Input any additional information and upload an icon
Deployment Type – Click Add
Deployment Type – General – Change the Type to Script Installer
Deployment Type – General Information – Provide a name and Admin Comment for your deployment type
Deployment Type – Content
Content Location – Select your content location (where you have saved the PowerShell Script)
Installation Program – Powershell.exe -ExecutionPolicy Bypass -File “..ps1” -ExecutionType Install
Uninstallation Program – owershell.exe -ExecutionPolicy Bypass -File “..ps1” -ExecutionType Uninstall
Detection Method – Select Use a custom script and click Edit
Script Type –Â PowerShell
Script Content – Paste the content of the script adding `Detect` to the header (If you are using a GitHub PAC key, you will also need to add this in)
Installation Behavior – Install for System (Leave the reset as default or change as you desire)
Dependencies & Requirements – Add any dependencies and requirements you wish
Click through the windows to complete the creation
Deploy the app to your desired collection
During the installation and the uninstallation of the apps, there is a transcript of the session that is by default stored in `C:WindowsLogsSoftware`. This will help in troubleshooting the install should you have any issues.
Other Blogs and Tools
Evergreen – Arron Parker
I came across this when putting a tweet out to see if this post was worth while, Well worth a read.
PatchMy PC – A leader in the 3rd Party Patching world
Now, this is not a community tool and it is licensed, however if you want to have this manage some of your Third Party apps with ConfigMGR, Intune or WSUS I would highly recommend them. This will save you a ton of time and help you on your way to having a fully patched estate.
Create Dynamic Application Packages with PowerShell
Published On: March 31, 2021Last Updated: January 5, 2024By David Brook15.1 min readViews: 525
TOC
Dynamic? In what way?
When I say the packages are dynamic, I mean that you don’t have to update the application package when a new version is released by the vendor.
There are caveats to both methods that we walk through below, there are also other community and paid for tools to do similar things.
However, the reason I wrote this post and started focusing on packaging applications in this was to avoid having support tickets when a new version is released, I also wanted to use this with ConfigMGR and Intune without the requirement of additional modules.
Show me the way!
Well lets show you a couple ways to do this, Web (HTML) Scraping and using the GitHub API.
Web Scraping
In my opinion, this is the most flawed method, as this replies on the website layout and/or table structure to stay the same as when you write your script. However, it is still an option and it works really well.
To be able to get the data from the tables in PowerShell we are going to need to use `Invoke-WebRequest`, Normally this would be super easy to use as it parses the HTML data for you. However, as this script will run as system in Intune you will need to launch it with `-UseBasicParsing` which complicates things a little more.
For this example we will use the Microsoft Remote Desktop Client, Are you ready? Lets begin. (You can achieve this using API Calls, However, this is a good example of table structure for Web Scraping)
Detection
Lets start by looking at the way we obtain the latest version and check it again the version in the registry.
As you can see from the image below, there is a version table right at the top of the web page.
If you press F12 and open the developer options, you can click through the HTML sections in the Elements tab and find the table like below;
As you can see from the snippet below we have to use a `HTMLContent` **COM** object to parse the HTML data so we can interact with the tables.
In it’s simplest form we get the RawContent and then write it to the IHTMLDocument2 object with the COM object, giving us the functionality  work with the tables.
Copy to Clipboard
Let’s take a closer look at the interaction with the tables, as you can see the variable $Tables uses the the $HTML variable which contains the COM object data to select everything with the tag of table (`$Tables = @($html.all.tags(‘table’))`). From this point it uses a for loop to gather the table data, until finally we decide which part of the table we want to use.
For example, We are focusing on the latest version, so if you run the for loop manually and look at $resultObjectin PowerShell it will return something like this;
From this point you can create a PSCustomObject with the table header you want. Now this is kind of over complicating it for this example as you could just return `$resultObject.’Latest version’` however, I use this loop for other methods and keeping it in this format helps me standardise the way I work, but it also gives you the ability to use if for other things too.
All of this is wrapped inside a function (`Get-LatestVersion`) as I plan on using the same script for the detection method as for the install, but I also like to re-check in my install script that the application definitely is not installed before the install action executes. If you look at the `Detect-Application` function you can see that I check both the 64-bit and 32-bit registry locations with an IFÂ statement based on the variables below;
Copy to Clipboard
The IF statement uses an `-or` operator, meaning if one of the conditions matches then run the code within the brackets below. As you can see from the variables the `$LatestVersion` uses the `Get-LatestVersion` function which is used to match the display version in the registry.
This the fundamental foundation of the operation, as we can now detect the application without using any additional modules in the next section we will look at the download and installation of the app.
Download Link
Now we know that we can Detect the application, lets look at obtaining the download link.
If you look at the below snippet, you can see we use a variable which calls a function to get the download link (`$DownloadLink = Get-DownloadLink`). For this to work there is a reliance on the the Variable `$Arch` been set, by default this is set to 64-bit. However, this is available as a command line parameter.
Copy to Clipboard
Lets take a look at the `Get-DownloadLink` function, the basics of getting the data and writing it to an HTMLCOM object is the same as the detection method, however this time we do not need to look at a table, we are specifically looking for a link which matched the `$Arch` variable.
Copy to Clipboard
If you look at the web page for the downloads you will see that the links are in an unordered list;
Again if you hit F12 and look at the html content behind the table, you will see the data we are looking for.
To get this using the script we simply run `($HTML.links | Where-Object {$_.InnerHTMl -Like “*$Arch*”}).href` simple right?
Download & Install
Before we look at the install function, lets look at the logic that calls the install.
Lets just assume you called the script with the Install execution type (`.`) or launched it without any parameters.
Lets look inside the default section (lines 17-27) below, firstly it will check if the latest version of the application is not installed using an IF statement, ELSEÂ return that it is already installed.
If the application is not installed it then proceeds to attempt the installation in a `try{} catch{}` statement. The basics of this is as it says, it will try the install, if it fails it will catch it and throw back the `Write-Error` text.
Copy to Clipboard
Lets take a look at the `Install-Application` function that is called in the statement.
Lets Break it down into stages.
Checks if the  `$DownloadPath` exists, if not it will try to create it.
Download the installer from the Link to the Download folder (`$DownloadPath`)
Install the MSI with the additional command line arguments `“$DownloadPath$InstallerName”” /qn /norestart /l* “”$DownloadPathRDINSTALL$(get-Date -format yyyy-MM-dd).log””`
Tip
When using double quotes (“) inside double quotes you must double them up.
For Example “The file is located: “”$VariablePath.txt”””
Copy to Clipboard
Uninstall
As we have a dynamic installation, we want the same for the uninstall right?
Well this is also achievable, take a look the the `Uninstall-Application` function below;
Copy to Clipboard
This is using some of the same logic as the **Detection** method, It checks both the 64-bit and the 32-bit registry keys to see if an application that is like the display name of our application.
If a registry entry is detected, it will obtain the Key Name in this case as we are dealing with an MSI. This is because the MSI Key name is the GUID, it will then build up the MSIEXEC arguments for the uninstall. After it has completed both steps it will then process with the uninstallation.
You will need to change the param block variable for `$ExecutionType` to `$ExecutionType = Detect` when using this as a detection method within Intune or ConfigMGR.
Copy to Clipboard
Your Content Goes That wraps up the Web Scraping method, I hope this proves useful when trying to make your apps more dynamic.
GitHub API
Using API calls is a better way to do dynamic updates. Some vendors host their content on GitHub as this provides build pipelines, wikis, projects and a whole host of other things. This is the method that is least likely to change, and if it does it will be documented using the GitHub API Docs.
For this example we are going to look at using Git for Windows, we will be using their GitHub Repo to query the version and also get the download.
Note
GitHub has a rate limit for the API calls, unautenticated calls has a rate limit of 60, GitHub authenticated accounts has a limit of 5000 and GitHub Enterprise accounts has a limit of 15000 calls.
Each time the script is launched it used1 call, so in terms of a detection and installation you will need a 2 api calls.
You will need to take this into account if you plan to package multiple applications in this way, you could use multiple accounts and randomise the PAC Key from an array, however this is something that should be highlighted.
If you look at line 30 above, you will notice that the versions matches the one on the latest release page.
Now we know what property within the API we are looking for and how it displays, we can head into PowerShell and start working on the detection.
First of all we need to get the latest version, to do this we first perform and API Call to get all of the information and store the information in the `$RestResult` variable.
Take a look at the below snippet;
Copy to Clipboard
The first thing to note on this snippet is the method it will use to connect to the API, If you specify a Personal Access Token with the `-GITPAC` parameter or via the variable in the script you will be able to have 5000 API calls for your application installs.
In short we specify the `$URL` variable and then run a GETÂ request with `Invoke-RestMethod` and specify that we want the output as `application/json`. Once it has the data we want to then format the `$LatestVersion` variable to return just the version number, for this we use the `.split()` operator, by default this splits on spaces, you can specify other characters to split it with by adding in something like `‘.’` and it would split the string at every point there is a dot. Now we have split the string, we want to select the index, for this example as the version number is at the end we want to select the index `[-1]`. If the index was at the start we would use `[0]`, feel free to experiment with this.
This variable is then used to call the `Detect-Application` function which will return `True` if the application is installed, otherwise it will return `null`.
Copy to Clipboard
Git Download Link
If you take a look back at the latest releases page, and scroll down to Assets, if you hover over one of them you will see the URL it links to in the bottom left-hand corner of your browser.
Now we know that we can Detect the application, lets look at obtaining the download link.
If you look at the script snippet below, you can see that we are still using the `$RestResult` to obtain the download link. To get the download link for the architecture you specify we first have to build up the `$EXEName` Variable, this uses the `$LatestVersion` and `$Arch` variables to bring the name together.
Once the name EXE Name is sorted, we then use this to get the link, by using the `Where-Object` function to select the download URL from the asset where the `$_.name` matches `$EXEName`.
Copy to Clipboard
Git Download & Install
The Install logic is the same as web scraping, however we will cover it here too so you don’t need to scroll up.
Lets just assume you called the script with the Install execution type (`.<ScriptName.ps1 -ExecutionType Install`) or launched it without any parameters.
Lets look inside the default section lines 15-27 below, firstly it will check if the latest version of the application is not installed using an IFÂ statement, ELSE return that it is already installed.
If the application is not installed it then proceeds to attempt the installation in a `try{} catch{}` statement. The basics of this is as it says, it will try the installation, if it fails it will catch it and throw back the `Write-Error` text.
Copy to Clipboard
Lets take a look at the `Install-Application` function that is called in the statement.
Lets Break it down into stages.
Checks if the  `$DownloadPath` exists, if not it will try to create it.
Download the installer from the Link to the Download folder (`$DownloadPath`)
Install the application with the additional command line arguments stored in the `$InstallArgs` variable.
Copy to Clipboard
Git Uninstall
As we have a dynamic installation, we want the same for the uninstall right?
Well this is also achievable, take a look the the `Uninstall-Application` function below;
Lets break this down,
Check if an application is installed with a display name like the string stored in `$DetectionString` (Checks both 64 and 32 Uninstall Keys)
If the application is installed, get the UninstallString from the key and store this in the `$UninstallEXE` variable.
Uninstall the application using the `$UninstallEXE` with the command line arguments stored in the `$UninstallArgs` variable.
Copy to Clipboard
Git Finished Script
If you compile all of the sections together with a little bit of formatting you will end up with a script like the one below.
You will need to change the param block variable for `$ExecutionType` to `$ExecutionType = Detect` when using this as a detection method within Intune or ConfigMGR.
Select App type Other>Windows app (Win32), Click Select
Click Select app package file, Click the Blue Folder icon to open the browse window
Select the .intunewin file you have created containing a copy of the script, Click Open and then click OK
Fill out the Name and Publisher mandatory fields, and any other fields you desire
Upload an icon if you desire, I would recommend doing this if you are deploying this to users via the Company Portal
Click Next
Enter your install command `powershell.exe -executionpolicy bypass “.<Script Name.ps1>”`
Enter your uninstall command `powershell.exe -executionpolicy bypass “.<Script Name.ps1>” -ExecutionType Uninstall`
Select your install behaviour as System
Select your desired restart behaviour, Adding custom return codes if required
Click Next
Complete your OS Requirements, At a minimum you need to specify the Architecture and the minimum OS Version (e.g. 1607/1703 etc.)
Click Next
For Detection rules, select Use a custom detection script
Script File: Browse to a copy of the Script where the ExecutionType was amended to`$ExecutionType = “Detect”`.
Assign the application to your desired group
Note
If you want to display the app in the company portal, it MUST be assigned to a group containing that user. Required Assignments will force the app to install, whereas Available will show this in the Company Portal. Click Next.
Click Create
ConfigMGR
Head over to your Software Library and Start Creating an application in your desired folder
General Tab, Select Manually Specify the application information
General Information – Input the information for your app
Software Center – Input any additional information and upload an icon
Deployment Type – Click Add
Deployment Type – General – Change the Type to Script Installer
Deployment Type – General Information – Provide a name and Admin Comment for your deployment type
Deployment Type – Content
Content Location – Select your content location (where you have saved the PowerShell Script)
Installation Program – Powershell.exe -ExecutionPolicy Bypass -File “..ps1” -ExecutionType Install
Uninstallation Program – owershell.exe -ExecutionPolicy Bypass -File “..ps1” -ExecutionType Uninstall
Detection Method – Select Use a custom script and click Edit
Script Type –Â PowerShell
Script Content – Paste the content of the script adding `Detect` to the header (If you are using a GitHub PAC key, you will also need to add this in)
Installation Behavior – Install for System (Leave the reset as default or change as you desire)
Dependencies & Requirements – Add any dependencies and requirements you wish
Click through the windows to complete the creation
Deploy the app to your desired collection
During the installation and the uninstallation of the apps, there is a transcript of the session that is by default stored in `C:WindowsLogsSoftware`. This will help in troubleshooting the install should you have any issues.
Other Blogs and Tools
Evergreen – Arron Parker
I came across this when putting a tweet out to see if this post was worth while, Well worth a read.
PatchMy PC – A leader in the 3rd Party Patching world
Now, this is not a community tool and it is licensed, however if you want to have this manage some of your Third Party apps with ConfigMGR, Intune or WSUS I would highly recommend them. This will save you a ton of time and help you on your way to having a fully patched estate.