Unified Application Management: Shell Apps Overview and Usage

Unified Application Management: Shell Apps Overview and Usage

The versatile Shell Apps feature offers the ability to create complex scripted deployment for Unified Application Management (UAM) deployment. Administrators can deploy multiple components via PowerShell and use the detection script feature to report success or failure information back to the UAM policy console for review.

This feature is intended for use with large or complex applications, or where deployment via Winget is not practical or desirable. Because the feature makes use of native PowerShell scripts, Winget can be called as part of an installation script if required.

Applications deployed using the Shell Apps feature are deployed exclusively in the device system context, avoiding the need for any service account.

This feature functions in a similar way to Nerdio Manager Scripted actions. If you are making use of scripted actions, there should be no complications when adopting the Shell Apps feature.

Prerequisites

PowerShell remoting (WSMan) must be enabled for the target devices.

Limitations

Shell Apps cannot be assigned to Intune devices if it contains secure variables.

Because this is a preview feature, the items noted below are not currently available. These are planned for future releases.

  • Secure variable support: As with scripted actions, secure variables provide the ability to deliver obfuscated, predefined items into the script.

  • SCCM integration: The ability to ingest scripted installations with their associated binaries from connected SCCM environments.

Getting Started

In Nerdio Manager, navigate to ApplicationsShell Apps to work with the Shell Apps feature. See Unified Application Management: Manage Shell Apps for details.

  • Notepad ++ (.EXE) and 7-Zip (MSI) are included when you get started.

  • Deployments are split into the following script areas: Detection, Install and Uninstall. All components are required to form a valid application package.

  • Each application package contains a switch labeled Public. This controls the application’s visibility within the UAM unified catalog page. It is recommended that this switch remains Off until you have completed your scripts, to avoid any potential deployment issues.

Please refer to the guidance below to understand how these example scripts function, and how you can reuse these to deploy your own applications using the Shell Apps feature.

File Packages

Shell Apps supports the upload of package files if required. This is useful if you want to include your installer files, and do not want your install routine to download files from an internet location. All application types supported by UAM are supported for Shell App installations.

Usage

If you are using an uploaded installer package, this can be called as part of the script by using the built in variable $Context.GetAttachedBinary(). For example, if you want to install an attached installer package, you may use the command:

Start-Process "$Context.GetAttachedBinary()" -Wait -ArgumentList "/S"

ZIP Archive Support

From v6.2 of Nerdio Manager, Shell Apps now supports the use of .ZIP archives to deploy a group of files to a desktop as part of the install process. Optionally, these .ZIP archives may be automatically extracted for application deployment tasks.

Usage

When importing a .ZIP archive on the Shell Apps ‘Files’ page, the additional ‘Unzip’ option is available. If selected, the script automatically extracts the compressed files to a temporary folder in C:\Windows\Temp on the target desktop.

After extraction, the script’s context is set to the created temporary directory automatically, therefore all installation tasks can be completed using relative paths in the script. For example, to call the file EXAMPLE.MSI from the Shell Apps installation script, simply use the command .\EXAMPLE.MSI as part of your install process. Arguments can be added as required. Additionally, other files and tasks, such as transforms, may also be called from the relative path.

Example: Notepad ++ (.EXE)

Detection Script

The detection script function provides the ability to create a ruleset that returns a positive or negative value for a specific query. We provide an example below of the detection script for a specific application (Notepad ++).

In this example, the PowerShell script efficiently checks for the existence of a specific program by verifying the existence of its installation folder and the program executable file within that folder.

Script Body

$basePath = $env:ProgramFiles

$programFolderName = "Notepad++"

$programFolderFullPath = Join-Path $basePath $programFolderName

 

$programFolderExists = Test-Path $programFolderFullPath

if (!$programFolderExists) {

return $false

}

 

$programFileName = 'notepad++.exe'

$programFileFullPath = Join-Path $programFolderFullPath $programFileName

 

return Test-Path $programFileFullPath

Components Breakdown

  1. Variables:

    • $basePath: Stores the path of the Program Files directory retrieved from the system environment variables ($env:ProgramFiles).

    • $programFolderName: Specifies the name of the program folder (in this case, "Notepad++").

    • $programFolderFullPath: Combines $basePath and $programFolderName using Join-Path to get the full path of the program folder.

  2. Checking Program Folder Existence:

    $programFolderExists = Test-Path $programFolderFullPath if (!$programFolderExists) { return $false }

    • Test-Path: Cmdlet used to determine whether a file or directory exists at a specified path.

    • $programFolderExists: Stores the result of the existence check for the program folder.

    • If the program folder doesn't exist (!$programFolderExists), the script returns $false, indicating that the program is not installed.

  3. Constructing Program File Path:

    $programFileName = 'notepad++.exe' $programFileFullPath = Join-Path $programFolderFullPath $programFileName

    • $programFileName: Specifies the name of the program executable file.

    • $programFileFullPath: Combines $programFolderFullPath and $programFileName using Join-Path to get the full path of the program executable file.

  4. Checking Program File Existence:

    return Test-Path $programFileFullPath

    • Checks if the program executable file exists at the specified path.

    • The script returns the result of this existence check, which indicates whether the program is installed ($true) or not ($false).

Installation Script

The installation script installs and configures all required components for the successful deployment of the application.

In this example, the PowerShell script automates the installation of Notepad++ by downloading the installer, executing it silently (/S argument), and cleaning up afterward.

Script Body

$basePath = $env:TEMP

$installerFolderName = "Notepad $(New-Guid)"

$installerFolderFullPath = Join-Path $basePath $installerFolderName

 

$installerFolderExists = Test-Path $installerFolderFullPath

if ($installerFolderExists) {

Remove-Item $installerFolderFullPath -Recurse -Force -ErrorAction Stop

}

 

New-Item -Path $basePath -Name $installerFolderName -ItemType Directory -Force | Out-Null

 

$installerName = 'installer.exe'

$installerFullPath = Join-Path $installerFolderFullPath $installerName

 

$installerUrl = "https://github.com/notepad-plus-plus/notepad-plus-plus/releases/download/v8.6/npp.8.6.Installer.x64.exe"

Invoke-WebRequest -URI $installerUrl -OutFile $installerFullPath

 

Start-Process "$installerFullPath" -Wait -ArgumentList "/S"

 

Remove-Item $installerFolderFullPath -Recurse -Force

 

$Context.Log('Install script completed')

Components Breakdown

  1. Variables:

    • $basePath: Stores the path of the temporary directory retrieved from the system environment variables ($env:TEMP).

    • $installerFolderName: Specifies the name of the folder for the Notepad++ installer. It includes a dynamically-generated GUID to ensure uniqueness.

    • $installerFolderFullPath: Combines $basePath and $installerFolderName using Join-Path to get the full path of the installer folder.

  2. Checking and Creating Installer Folder:

    $installerFolderExists = Test-Path $installerFolderFullPath if ($installerFolderExists) { Remove-Item $installerFolderFullPath -Recurse -Force -ErrorAction Stop } New-Item -Path $basePath -Name $installerFolderName -ItemType Directory -Force | Out-Null

    • Checks if the installer folder already exists. If it does, it removes it recursively and forcefully to ensure a clean installation environment.

    • Creates a new directory for the installer using New-Item cmdlet.

  3. Downloading Installer:

    $installerUrl = "https://github.com/notepad-plus-plus/notepad-plus-plus/releases/download/v8.6/npp.8.6.Installer.x64.exe" Invoke-WebRequest -URI $installerUrl -OutFile $installerFullPath

    • Specifies the URL of the Notepad++ installer.

    • Downloads the installer executable using Invoke-WebRequest and saves it to the specified path.

  4. Starting Installation Process:

    Start-Process "$installerFullPath" -Wait -ArgumentList "/S"

    • Initiates the installation process by starting the installer executable. The -Wait parameter ensures that the script waits for the installation to complete before proceeding.

  5. Cleaning Up:

    Remove-Item $installerFolderFullPath -Recurse -Force

    • Removes the installer folder and its contents after the installation is complete, ensuring a clean environment.

  6. Logging:

    $Context.Log('Install script completed')

    • Logs a message indicating that the installation script has completed. This function can also log a message which is later available on the Policy Details page. This can be used as $Context.Log(string $message).

Uninstall Script

The uninstall script provides the ability to remove a specified application as part of the policy assessment process. The detection script is re-used to confirm the specified detection items are not present.

In this example, the PowerShell script automates the uninstallation of Notepad++ by checking for the existence of both the program folder and the uninstaller executable. If both are found, it executes the uninstaller silently (/S argument).

Script Body

$basePath = $env:ProgramFiles

$programFolderName = "Notepad++"

$programFolderFullPath = Join-Path $basePath $programFolderName

 

$programFolderExists = Test-Path $programFolderFullPath

if (!$programFolderExists) {

throw "Notepad++ is not found"

}

 

$uninstallerFileName = 'uninstall.exe'

$uninstallerFileFullPath = Join-Path $programFolderFullPath $uninstallerFileName

 

$uninstallerFileExists = Test-Path $uninstallerFileFullPath

if (!$uninstallerFileExists) {

throw "Notepad++ uninstaller is not found"

}

 

Start-Process "$uninstallerFileFullPath" -Wait -ArgumentList "/S"

 

$Context.Log('Uninstall script completed')

Components Breakdown

  1. Variables:

    • $basePath: Stores the path of the Program Files directory retrieved from the system environment variables ($env:ProgramFiles).

    • $programFolderName: Specifies the name of the program folder (in this case, "Notepad++").

    • $programFolderFullPath: Combines $basePath and $programFolderName using Join-Path to get the full path of the program folder.

  2. Checking Program Folder Existence:

    $programFolderExists = Test-Path $programFolderFullPath if (!$programFolderExists) { throw "Notepad++ is not found" }

    • Test-Path: Cmdlet used to determine whether a file or directory exists at a specified path.

    • Throws an error if the Notepad++ program folder doesn't exist, indicating that Notepad++ is not installed.

  3. Checking Uninstaller File Existence:

    $uninstallerFileName = 'uninstall.exe' $uninstallerFileFullPath = Join-Path $programFolderFullPath $uninstallerFileName $uninstallerFileExists = Test-Path $uninstallerFileFullPath if (!$uninstallerFileExists) { throw "Notepad++ uninstaller is not found" }

    • Constructs the full path to the Notepad++ uninstaller executable (uninstall.exe).

    • Checks if the uninstaller executable exists. If it doesn't, throws an error indicating that the uninstaller is not found.

  4. Starting Uninstallation Process:

    Start-Process "$uninstallerFileFullPath" -Wait -ArgumentList "/S"

    • Initiates the uninstallation process by starting the uninstaller executable. The -Wait parameter ensures that the script waits for the uninstallation to complete before proceeding.

  5. Logging:

    $Context.Log('Uninstall script completed')

    • Logs a message indicating that the uninstallation script has completed. This function can also log a message which is later available on the Policy Details page. This can be used as $Context.Log(string $message).

Example: 7-Zip (.MSI)

Detection Script

The detection script function provides the ability to create a ruleset which returns a positive or negative value for a specific query. We provide an example below of the detection script for a specific application (7-Zip). This PowerShell script checks for the existence of a specific program by verifying the presence of its uninstall information in the Windows Registry.

Script Body

$basePath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"

$productCode = '{23170F69-40C1-2702-2201-000001000000}'

$programRegistryPath = Join-Path $basePath $productCode

 

return Test-Path $programRegistryPath

Components Breakdown

  1. Variables:

    • $basePath: Stores the base path in the Windows Registry where the uninstall information for installed programs is typically stored. In this case, it points to HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall, which is the registry location for installed programs for all users.

    • $productCode: Specifies the unique product code associated with the program being checked. This code is typically a GUID (Globally Unique Identifier) and is used to identify the program in the registry.

    • $programRegistryPath: Combines the base path ($basePath) and the product code ($productCode) using Join-Path to form the full registry path to the program's uninstall information.

  2. Checking Program Registry Key Existence:

    return Test-Path $programRegistryPath

    • Test-Path: Cmdlet used to determine whether a file, directory, or in this case, a registry key exists at a specified path.

    • The script returns the result of this existence check, which indicates whether the program is installed (registry key exists) or not.

Installation Script

The installation script installs and configures all required components for the successful deployment of the application. This PowerShell script automates the installation of 7-Zip by downloading the installer MSI file, executing it silently (/qn argument), and cleaning up afterward.

Script Body

$basePath = $env:TEMP

$installerFolderName = "7zip $(New-Guid)"

$installerFolderFullPath = Join-Path $basePath $installerFolderName

 

$installerFolderExists = Test-Path $installerFolderFullPath

if ($installerFolderExists) {

Remove-Item $installerFolderFullPath -Recurse -Force -ErrorAction Stop

}

 

New-Item -Path $basePath -Name $installerFolderName -ItemType Directory -Force | Out-Null

 

$installerName = 'installer.msi'

$installerFullPath = Join-Path $installerFolderFullPath $installerName

 

$installerUrl = "https://7-zip.org/a/7z2201-x64.msi"

Invoke-WebRequest -URI $installerUrl -OutFile $installerFullPath

 

Start-Process msiexec -ArgumentList "/i `"$installerFullPath`" /qn" -Wait

 

Remove-Item $installerFolderFullPath -Recurse -Force

 

$Context.Log('Install script completed')

Components Breakdown

  1. Variables:

    • $basePath: Stores the path of the temporary directory retrieved from the system environment variables ($env:TEMP).

    • $installerFolderName: Specifies the name of the folder for the 7-Zip installer. It includes a dynamically generated GUID to ensure uniqueness.

    • $installerFolderFullPath: Combines $basePath and $installerFolderName using Join-Path to get the full path of the installer folder.

  2. Checking and Creating Installer Folder:

    $installerFolderExists = Test-Path $installerFolderFullPath if ($installerFolderExists) { Remove-Item $installerFolderFullPath -Recurse -Force -ErrorAction Stop } New-Item -Path $basePath -Name $installerFolderName -ItemType Directory -Force | Out-Null

    • Checks if the installer folder already exists. If it does, it removes it recursively and forcefully to ensure a clean installation environment.

    • Creates a new directory for the installer using New-Item cmdlet.

  3. Downloading Installer:

    $installerName = 'installer.msi' $installerFullPath = Join-Path $installerFolderFullPath $installerName $installerUrl = "https://7-zip.org/a/7z2201-x64.msi" Invoke-WebRequest -URI $installerUrl -OutFile $installerFullPath

    • Specifies the URL of the 7-Zip installer.

    • Downloads the installer MSI file using Invoke-WebRequest and saves it to the specified path.

  4. Starting Installation Process:

    Start-Process msiexec -ArgumentList "/i `"$installerFullPath`" /qn" -Wait

    • Initiates the installation process by starting the msiexec utility with the /i (install) flag and the path to the MSI installer file ($installerFullPath). The /qn parameter installs silently without displaying any user interface.

  5. Cleaning Up:

    Remove-Item $installerFolderFullPath -Recurse -Force

    • Removes the installer folder and its contents after the installation is complete, ensuring a clean environment.

  6. Logging:

    $Context.Log('Install script completed')

    • Logs a message indicating that the installation script has completed. This function can also log a message which is later available on policy Details page. This can be used as $Context.Log(string $message).

Uninstall Script

The uninstall script provides the ability to remove a specified application as part of the policy assessment process. The detection script can be re-used to confirm the specified detection items are not present. This example PowerShell script automates the uninstallation of 7-Zip by checking for the existence of its uninstallation registry key and initiating the uninstallation process silently.

Script Body

$basePath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"

$productCode = '{23170F69-40C1-2702-2201-000001000000}'

$programRegistryPath = Join-Path $basePath $productCode

 

$programExists = Test-Path $programRegistryPath

if (!$programExists) {

throw "7zip is not found"

}

 

Start-Process msiexec -ArgumentList "/x `"$productCode`" /qn" -Wait

 

$Context.Log('Uninstall script completed')

Components Breakdown

  1. Variables:

    • $basePath: Stores the base path in the Windows Registry where uninstall information for installed programs is typically stored. In this case, it points to HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall, which is the registry location for installed programs for all users.

    • $productCode: Specifies the unique product code associated with 7-Zip. This code is typically a GUID (Globally Unique Identifier) and is used to identify the program in the registry.

    • $programRegistryPath: Combines the base path ($basePath) and the product code ($productCode) using Join-Path to form the full registry path to the program's uninstall information.

  2. Checking Program Registry Key Existence:

    $programExists = Test-Path $programRegistryPath if (!$programExists) { throw "7zip is not found" }

    • Test-Path: Cmdlet used to determine whether a registry key exists at a specified path.

    • Throws an error if the registry key for 7-Zip doesn't exist, indicating that 7-Zip is not installed.

  3. Starting Uninstallation Process:

    Start-Process msiexec -ArgumentList "/x `"$productCode`" /qn" -Wait

    • Initiates the uninstallation process by starting the msiexec utility with the /x (uninstall) flag and the product code ($productCode). The /qn parameter uninstalls silently without displaying any user interface.

  4. Logging:

    $Context.Log('Uninstall script completed')

    • Logs a message indicating that the uninstallation script has completed. This function can also log a message which is later available on the Policy Details page. This can be used as $Context.Log(string $message).

Versioning

As of Nerdio Manager v6.6, Shell Apps now supports versioning. This section provides guidance for creating and using application versions within the user interface.

Version Configuration UI and Flow Examples

See Unified Application Management: Shell Apps Technical Reference Guide for details about the version configuration UI and flow examples.

Versioning Example

The example below is provided as a suggested approach to managing versions within Shell Apps. This is a very flexible feature, and customers may make use of a range of different detected environmental conditions to determine the installation status of a package. In this example, we use file version as our detection rule, however this function is equally applicable to registry keys, files, and folders.

Detection Script

The detection script below interrogates the executable file at the path specified and returns the Product Version information.

Note: The detection script must be configured to return a result in the version format x.x.x.x. For a given version, the version Name in the version properties must be identical to the value returned by the detection script for any associated polices to assess the application's status.

$nppDirPath = Join-Path $env:ProgramFiles "Notepad++"

$exePath = Join-Path $nppDirPath 'notepad++.exe'

if (!(Test-Path $exePath)) {

return $null # nothing is installed

}

# picking version from .exe metadata

return (Get-Item $exePath).VersionInfo.ProductVersion

Installation Script

The installation script below installs the target version of the application, which is defined in the UAM policy and available in the script as $Context.TargetVersion.

$installerTempDir = Join-Path $env:TEMP "Notepad $(New-Guid)"

$installerPath = Join-Path $installerTempDir 'installer.exe'

# creating directory in Temp folder

New-Item -Path $installerTempDir -ItemType Directory -Force | Out-Null

# generating url dynamically based on target version

# configured in deployment policy

$version = $Context.TargetVersion

$installerUrl = "https://github.com/notepad-plus-plus/notepad-plus-plus/releases/download/v$version/npp.$version.Installer.x64.exe"

# downloading the file and running install with silent switch

Invoke-WebRequest -URI $installerUrl -OutFile $installerPath

Start-Process "$installerPath" -Wait -ArgumentList "/S"

# cleaning up after ourselves

Remove-Item $installerTempDir -Recurse -Force

Uninstall Script

The uninstall script below simply removes the application using the routine defined if the detection rule conditions are met.

$nppDirPath = Join-Path $env:ProgramFiles "Notepad++"

$uninstallerPath = Join-Path $nppDirPath 'uninstall.exe'

Start-Process "$uninstallerPath" -Wait -ArgumentList "/S"

Was this article helpful?

0 out of 0 found this helpful
Have more questions? Submit a request

Comments (0 comments)

Article is closed for comments.