Overview of Run Script Actions

Patch Management helps patch servers, endpoints and cloud devices seamlessly through Qualys Cloud Agent. Along with patch deployment, it also provides Run Scripts capability that allows you to execute custom scripts for automation as per your requirement.

With Run Scripts, you can: 

  • Run PowerShell scripts on Windows endpoints.
  • Run Shell scripts on Linux endpoints. 
  •  Run Shell or AppleScript scripts on macOS endpoints.

Configure scripts as pre-actions (run before patching) or post-actions (run after patching) in the patch deployment job. Each patch job supports up to 5 pre-actions and  post-actions, providing you a better control over pre-checks, environment preparation, post-patch validation and clean up tasks. For example, you can configure a system health check run-script pre-action before deploying the patches on staging and production environments. For more information on how to configure the pre and post actions in a patch job, see Creating Patch Job for Windows Assets.

Pre and Post Actions Execution Order

1. Execution of all pre-actions

2. Patch Installation

3. Execution of all post-actions

4. (Optional) Reboot (if required)

If required, a reboot is performed only once, at the end of the patch job execution, after all the actions and patches are processed. 

Exit Codes

Each script must end with an exit code so the Qualys Cloud Agent can determine the result and decide the next action. The table below lists the supported exit codes.

Exit Code Description Job Continues?  Reboot required? 
0 Script executed successfully. Yes No
1 Script execution failed.  Yes No
10 Script executed successfully, reboot required. Yes Yes
11 Script execution failed, reboot required. Yes Yes
12 Abort the job. All remaining actions and patches are skipped. No NA
101 Skip this action, the job and remaining actions continue.  Yes NA

The following are the detailed descriptions for the exit codes for Run Script actions: 

Exit Code 0 and 1

Exit Code 0 (Success) : Indicates that the script executed successfully and the job proceeds to the next action without interruption. Depending on the status of other scripts and patches, the job displays a Completed or Completed with Failure status. 

Applicable Scenarios: Environment validation passed, a service started or stopped successfully, a configuration change was applied, and so on.

Exit Code 1 (Failure): Indicates that the script encountered an error and failed, but the job continues with the next action or phase. The patch job displays a Completed with Failure status. This allows non-critical failures to be recorded without stopping the entire patch deployment cycle.

Applicable Scenarios: A pre-check identified a warning condition that should be logged but does not require stopping the job.

You can use the exit codes individually or in combination in your scripts as per your requirement. 

The following example demonstrates the use of both exit code 0 and 1. This sample script checks if the software is installed or not. A return code of 0 indicates that a new version of the software can be installed, while a return code of 1 indicates that installation is not required, as the earlier version is already available on the system.

Example: To detect if Notepad++ version 8.1.2 can be installedExample: To detect if Notepad++ version 8.1.2 can be installed

Exit Code 0 and 1

# Sample pre-detection script for Qualys Patch Manager       *
#     This sample script demonstrates detection of installed  *
#     software. It returns 0 if software can be installed    *
#     and returns 1 if installation is not required          *
#                                                            *
#       Return Code                                          *
#        0 - success                                         *
#        1 - failure                                         *
#*************************************************************
# required parameter
[string]$SoftwareName = "notepad++*"
[string]$SoftwareVersion = "8.1.2"
# Print OS info for asset information and troubleshooting
Write-Host "Gathering OS Information..."
$osInfo = Get-CimInstance Win32_OperatingSystem | Select-Object Caption, Version, ServicePackMajorVersion, OSArchitecture, CSName
$properties = @{
    Caption                 = $osInfo.Caption
    Version                 = $osInfo.Version
    ServicePackMajorVersion = $osInfo.ServicePackMajorVersion
    OSArchitecture          = $osInfo.OSArchitecture
    HostName                = $osInfo.CSNAme
}
$obj = New-Object -TypeName PSObject -Property $properties
Write-Output $obj
Write-Host "Looking for package installation...`n"
# Check install key in both wow64 and normal path. Application can be either 32-bit or 64-bit.
$32bit = Get-ItemProperty HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*
$64bit = Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*
$programs = $32bit + $64bit
$packageFound = $false
foreach ($program in $programs) {
    $program = Write-Output $program | Where-Object Displayname -like $SoftwareName
    if ($null -ne $program.DisplayName) {
        $LastModified = (Get-Item $program.uninstallstring).lastwritetime
        $properties = @{
            ProgramName     = $program.DisplayName
            Publisher       = $program.Publisher
            Version         = $program.DisplayVersion
            UninstallString = $program.UninstallString
            LastModified    = $LastModified
        }
        $package = New-Object -TypeName PSObject -Property $properties
        Write-Output $package
        $packageFound = $true
        break;
    }
}
if ($packageFound -eq $false) {
    Write-Host "No installed package found"
    # Package needs to be isntalled
    exit 0
}
Write-Host "Checking installed software version..."
[System.Version] $installedVersion = $package.Version
[System.Version] $ExpectedVersion = $SoftwareVersion
if ($installedVersion -lt $ExpectedVersion) {
    Write-Host "Installed software is older than required version. Patch upgrade needed."
    exit 0
}
else {
    Write-Host "Installed software is up to date."
    exit 1
}>

Exit Code 10 and 11

Exit Code 10 (Success with Reboot Required): Indicates that the script executed successfully and a system reboot is required. The reboot is triggered only after the job execution is completed. 

Key Behaviors (for Codes 10 and 11) : 

  • If multiple actions exit with code 10 or 11, the system performs only one reboot at the end of the job.
  • Reboot requests from Post Actions are ignored. Only pre-actions and patch installations can request a reboot.
  • The job continues through all remaining actions and patch installations before the reboot occurs. 

Applicable Scenario: A pre-action installs a prerequisite component, such as a run-time or driver, that requires a reboot to take effect.

Exit Code 11 (Failure with Reboot Required) : Indicates that the script execution failed or any errors encountered while script execution, and a system reboot is still required. Key behaviors stated for exit code 10 also apply for exit code 11. 
Applicable Scenario: A pre-action attempted a configuration change that partially succeeded and left the system in a state that requires a reboot despite the failure.

You can use the exit codes individually or in combination in your scripts as per your requirement. 

The following script is an example of both exit codes 10 and 11. This sample script demonstrates the installation of software. If the code returned is 0, the software is installed successfully, and if the code returned is 1, the installation fails.

Example: To install Notepad++ version 8.1.2Example: To install Notepad++ version 8.1.2

Exit Code 10 and 11

#*************************************************************
# Sample install script for Qualys Patch Manager             *
#     This sample script demonstrate installation of         *
#     software. It returns 0 if software is installed        *
#     successfully and returns 1 if installation fails.      *
#       Return Code                                          *
#        0 - success                                         *
#        1 - failure                                         *
#*************************************************************
[string]$PackageName = "npp.8.1.2.Installer.x64.exe"
[string]$InstallerLocation = $Env:ProgramData + "\Qualys\QualysAgent\PatchManagement\PatchDownloads\"
[string]$Arguments = "/S"
#set this 1 if reboot is required after package installation
[int]$RebootFlag = 1
function Get-QualysScriptReturn {
    param (
        [ValidateRange(0, 1)]
        [Int]
        $Success_Code,
       [ValidateRange(0, 1)]
        [Int]
        $RebootFlag
    )
    return $Success_Code + ($RebootFlag * 10)
}
function Start-Program {
    param (
        [ValidateNotNullOrEmpty()]
        [string]
        $ProgramFullPath,
        [Parameter(Mandatory = $false)]
        [string]
        $Arguments        
    )   
    try {   
        $pinfo = New-Object System.Diagnostics.ProcessStartInfo
        $pinfo.FileName = $ProgramFullPath
        $pinfo.RedirectStandardError = $true
        $pinfo.RedirectStandardOutput = $true
        $pinfo.UseShellExecute = $false
    
        if ($null -ne $Arguments) {
            $pinfo.Arguments = $Arguments
        }     
        $pinfo.WorkingDirectory = Get-Location
        $p = New-Object System.Diagnostics.Process
        $p.StartInfo = $pinfo
        $p.Start() | Out-Null
        $p.WaitForExit()
        $stdout = $p.StandardOutput.ReadToEnd()
        $stderr = $p.StandardError.ReadToEnd()
   
        Write-Host "Process Stdout:`n $stdout"
        Write-Host "Process Stderr:`n $stderr"
        Write-Host "exit code: "$p.ExitCode
        return $p.ExitCode
    }
    catch {
        Write-Host -ForegroundColor DarkRed "Failed to execute program" $_.Exception.Message
        return 1
    }
}
Write-Host "Running install script to install -"$PackageName
Write-Host "Looking package installer..."
$InstallerFullPath = $InstallerLocation + $PackageName
if (Test-Path $InstallerFullPath) {
    Write-Host "Found installer at "$InstallerFullPath
}
else {
    Write-Host "Installer does not exist at "$InstallerFullPath
    exit 1;
}
Write-Host "Launching installer..."
$ReturnCode = Start-Program $InstallerFullPath $Arguments
if ($ReturnCode -eq 0) {
    Write-Host -ForegroundColor Green "Installation successfull..."
    exit Get-QualysScriptReturn 0 $RebootFlag;
}
else {
    Write-Host -ForegroundColor DarkRed "Installation failed. See output for more details."
    exit 1
}

Exit Code 12 (Abort Job)

This is the most critical exit code. When a script exits with code 12:

  1. The current action is marked as failed with exit code 12.
  2. All remaining Pre and Post Actions are skipped and marked with system-assigned exit code 101.
  3. Patch installation is skipped.

The system assigns exit code 101 to all skipped actions to indicate that the scripts were not executed because the job was aborted.

For example, suppose a job has five pre-actions configured, the first pre-action executes successfully, but the system gives an exit code 12 in the second pre-action. Then the remaining three pre/post-actions, do not execute and the system marks them as Skipped with exit code 101. As a result, the patches in the job are skipped and job is aborted. The patch job status appears as Completed with Failure.

The following script is an example that returns exit code 12. The Healthcare providers often use Electronic Medical Record (EMR) systems that run regular backups. If patching starts during these backup windows, it can interfere with data integrity and system performance. So avoid this issue, you can configure a pre-action in the patch job, that checks if the backup flag is On, the script returns exit code 12 and the pre-action executes and patch job execution is aborted.  

Example: EMR Servers during Backup WindowsExample: EMR Servers during Backup Windows

The scheduled tasks update the following status file to indicate backup activity. 

# Before backup
Set-Content -Path "C:\AppStatus\backup.status" -Value "BackupMode=ON"
# After backup
Set-Content -Path "C:\AppStatus\backup.status" -Value "BackupMode=OFF"

The pre-action PowerShell script checks this status:

# pre_patch_check.ps1

$StatusFile = "C:\AppStatus\backup.status"
if (-Not (Test-Path $StatusFile)) {
    Write-Output "Status file not found – assuming unsafe to patch."
    exit 12
}

$StatusLine = Get-Content $StatusFile | Where-Object { $_ -match "BackupMode" }

if ($StatusLine -match "BackupMode\s*=\s*ON") {
    Write-Output "Server is in Backup Mode – canceling patch job."
    exit 12
} else {
    Write-Output "No backup in progress – safe to patch."
    exit 0
}

Exit Code 101 (Skip this Action)

Exit code 101 has two meanings, depending on how it is used.

Context Description
User specified in a script The current action is intentionally skipped. The job and remaining actions continue normally. 
If a job has five pre-actions configured and the second pre-action is skipped with an exit code of 101, then the consecutive three pre-actions continue to execute. 
System-assigned Automatically assigned to actions skipped due to a preceding exit code 12 (abort). If a job has five pre-actions configured, and the if first action returns code 12, the remaining actions are skipped with exit code 101 and the job is aborted. 

When explicitly used in a script, exit code 101 enables conditional logic. A script can evaluate the environment at run-time and skip itself if execution is not required, without affecting the rest of the job.

Applicable Scenario (user-specified): A pre-action checks whether a required prerequisite is already installed. If it is present, the script exits with code 101 to avoid redundant work.

Applicable Scenario (system-assigned): A preceding pre-action exits with code 12. The system assigns exit code 101 to all remaining actions to indicate that they were skipped and not executed.

If a job has five pre-actions configured and the second pre-action is skipped with an exit code of 101, then the consecutive three pre-actions continue to execute. The patch job status is Completed with Failure, since exit code 101 is generated only after a pre-action fails with exit code 12.

When the previous action returns the exit code 12, the system returns exit code 101 directly. 

Exit Code Flowchart

The following figure illustrates the flow of the flow of the exit codes while script execution. 

Flowchart that shows flow of all exit codes

Reboot Behavior Summary

The following is the reboot behavior summary:

  • Single reboot: Regardless of how many actions request a reboot (exit code 10 or 11), the system performs only one reboot.
  • Deferred until job completion: The reboot occurs only after all actions and patch installations are complete.
  • Post-actions cannot trigger reboot: Reboot requests from Post Actions are ignored. Only Pre Actions and patch installations can trigger a reboot.

Best Practices

  • Use exit  codes  0 and 1 for standard success and failure results.
    Always return an explicit exit code from your scripts. Do not rely on the shell’s default behavior, as it can produce inconsistent results.
  • Use exit code 12 only for critical failures.
    Exit code 12 aborts the entire job. Use it only when continuing the job could negatively impact the endpoint.
  • Use exit code 101 for conditional execution.
    Perform a quick check at the beginning of the script and exit with code 101 if the action is not required. This keeps scripts simple and avoids unnecessary execution.
  • Plan reboot-dependent actions carefully.
    Reboots occur only at the end of the job. If an action depends on a reboot, place it in a separate job instead of the same job sequence.
  • Keep scripts idempotent.
    Jobs may run again during retries. Ensure your scripts can execute multiple times without causing issues or duplicate changes.

  • Test scripts before production deployment.
    Validate scripts and exit code behavior on a representative set of endpoints before using them in production patch jobs. You can also use the Linked Job feature in Patch Management to first deploy and test jobs, including scripts, on a staging asset group. After the linked job succeeds, the main job automatically runs on production assets, enabling a safer phased roll out.

Additional Examples

Windows Pre Action: Disk Space CheckWindows Pre Action: Disk Space Check

PowerShell (Windows)

# Check if the system drive has at least 5 GB free
$freeSpaceGB = (Get-PSDrive -Name C).Free / 1GB

if ($freeSpaceGB -lt 5) {
    Write-Output "CRITICAL: Only $([math]::Round($freeSpaceGB,2)) GB free on C:. Aborting job."
    exit 12
}

Write-Output "Disk space check passed. $([math]::Round($freeSpaceGB,2)) GB available."
exit 0

Linux Pre Action: Stop Application Before PatchingLinux Pre Action: Stop Application Before Patching

Shell (Linux)

#!/bin/bash
# Stop the application service before patching
systemctl stop myapp.service

if [ $? -eq 0 ]; then
    echo "Service myapp stopped successfully."
    exit 0
else
    echo "Failed to stop myapp service. Aborting job."
    exit 12
fi

Linux Pre Action: Conditional SkipLinux Pre Action: Conditional Skip

Shell (Linux)

#!/bin/bash
# Skip if the required package is already at the desired version
CURRENT_VERSION=$(rpm -q targetpkg --qf '%{VERSION}')
DESIRED_VERSION="2.4.1"

if [ "$CURRENT_VERSION" == "$DESIRED_VERSION" ]; then
    echo "targetpkg is already at version $DESIRED_VERSION. Skipping."
    exit 101
fi

# Perform prerequisite installation
yum install -y targetpkg-prerequisite
if [ $? -eq 0 ]; then
    echo "Prerequisite installed. Reboot required."
    exit 10
else
    echo "Prerequisite installation failed."
    exit 1
fi

macOS Post Action: Notify UsermacOS Post Action: Notify User

AppleScript (macOS)

#!/bin/bash
# Display a notification to the logged-in user after patching
osascript -e 'display notification "Patch deployment complete. Please save your work." with title "IT Patch Management"'

if [ $? -eq 0 ]; then
    exit 0
else
    exit 1
fi