Remove File (Package)
A parameterized Tanium package that deletes one file from Windows endpoints.
The operator types the file name a second time before anything is deleted. The script also refuses files in Windows system folders, boot files, and anything in the Tanium Client folder, whatever is typed.
Use this package to remove a single file that a scan or a ticket has flagged, such as a vulnerable DLL left behind by an old install, a stale configuration file, or a log file that has grown too large. To delete a whole folder, use Remove Folder instead. To find where a file is first, use Find File by Name.
Test status
Tested on Windows 11 Pro 24H2 with Windows PowerShell 5.1. Each test ran the script through cmd.exe from the script's own folder with URL-encoded parameters, the same way a Tanium package runs it. The tests ran as a standard user, not as SYSTEM, and have not yet been repeated through a Tanium action. See Test results.
Why there is a confirmation parameter
The package runs as SYSTEM, which can delete almost any file on the endpoint. A typo, or a path pasted from the wrong ticket, would delete the wrong file without any prompt.
The Confirm Name parameter must match the file name, extension included. For C:\ProgramData\ExampleApp\example.dll, the operator types example.dll. Typing example without the extension does not match. If the two do not match, nothing is deleted and the action log says why.
This catches careless mistakes. It does not stop someone who types the wrong path twice, which is why the script also has a list of locations it refuses to touch.
Parameters
| Parameter | Passed as | Required | What to enter |
|---|---|---|---|
| File Path | $1 |
Yes | Full local path of the file. Example: C:\ProgramData\ExampleApp\example.dll |
| Confirm Name | $2 |
Yes | The file name with its extension, typed again. Example: example.dll. Not case sensitive. |
- The path must start with a drive letter, such as
C:\. Network paths (\\server\share\file.txt) and relative paths are rejected. - Wildcards (
*and?) are rejected. The package deletes exactly one file per action. - A path that ends with a backslash is treated as a folder and rejected.
Tanium replaces $1 and $2 in the command line with the values you enter, in the order the parameters are defined in the package. File Path must be the first parameter and Confirm Name the second.
What it refuses to delete
The script stops with exit code 4, and deletes nothing, when the file is:
- Anywhere under
C:\Windows\System32,C:\Windows\SysWOW64,C:\Windows\WinSxS, orC:\Windows\Boot. - Anywhere under
C:\BootorC:\Recovery. - Directly in
C:\, such aspagefile.sys,hiberfil.sys, orbootmgr. - Directly in
C:\Windows, such asexplorer.exe. - Anywhere in the Tanium Client folder.
- A symbolic link.
Paths are resolved before they are checked, so C:\Temp\..\Windows\System32\notepad.exe is caught as a System32 file.
Files under C:\Program Files and C:\ProgramData are allowed on purpose. Removing a single file left behind by an application, such as an old DLL flagged by a vulnerability scan, is one of the main uses for this package.
Exit codes
| Exit code | Meaning |
|---|---|
0 |
The file was deleted, or it was not there to begin with. |
1 |
The delete failed. Usually the file is in use by a running program or service. |
2 |
File Path was blank, not a full local path, contained a wildcard, ended with a backslash, or pointed to a folder. |
3 |
Confirm Name did not match the file name. Nothing was deleted. |
4 |
The file is in a protected location, or it is a symbolic link. Nothing was deleted. |
In Action History, exit codes other than 0 can show as Failed. For codes 2, 3, and 4, that is the script working as intended: it stopped before deleting anything.
Requirements
- Windows endpoints with Windows PowerShell 5.1.
- A Tanium account with permission to create packages in a content set and deploy actions to the target computers.
Create the package
Menu labels can differ slightly between Tanium versions.
- Save the script at the bottom of this page as
Remove-File.ps1. - In the Tanium Console, go to Administration > Content > Packages.
- Click Create Package.
- Fill in the fields:
- Package Display Name:
Remove File(add your team's prefix if you use one). - Content Set: the content set your team uses for custom content.
- Command:
- Command Timeout:
5minutes. The script normally finishes in a few seconds.
- Package Display Name:
- Under Files, click Add, choose Local File, and upload
Remove-File.ps1. - Expand Parameters. Under Parameter Inputs, add a Text Input parameter. It shows in the list as
$1.- Label:
File Path - Provide Help Text: select it and enter
Full path of the file to delete, for example C:\ProgramData\ExampleApp\example.dll
- Label:
- Add a second Text Input parameter. It shows in the list as
$2.- Label:
Confirm Name - Provide Help Text: select it and enter
Type the file name again with its extension, for example example.dll
- Label:
- In the Preview panel on the right, confirm you see File Path followed by Confirm Name.
- Click Save.
Deploy the package
- In Interact, ask a question that returns the computers you want to clean up. Example:
- Select the computers in the results grid.
- Click Deploy Action.
- In Deployment Package, search for and select
Remove File. - In File Path, enter the full path, for example
C:\ProgramData\ExampleApp\example.dll. - In Confirm Name, type the file name again, for example
example.dll. - Under the schedule, leave it as a one-time action.
- Click Show preview to continue, review the targets, then click Deploy Action.
Endpoints that do not have the file finish with exit code 0 and log File not found. Nothing to do., so it is safe to target a broad group.
View the results
One endpoint
- Open the action from Administration > Actions > Action History.
- Select an endpoint.
- Open its action log. The script output appears between the
Command Lineline and theCompletedline.
File deleted. The size, date, and attributes are logged before the delete, so there is a record of what was removed:
2026-10-08 11:19:40 [INFO] Requested file: 'C:\ProgramData\ExampleApp\example.dll'
2026-10-08 11:19:40 [INFO] Confirmation value: 'example.dll'
2026-10-08 11:19:40 [INFO] Resolved file: 'C:\ProgramData\ExampleApp\example.dll'
2026-10-08 11:19:40 [INFO] Confirmation matched file name 'example.dll'.
2026-10-08 11:19:40 [INFO] Deleting 'C:\ProgramData\ExampleApp\example.dll' (348,160 bytes, modified 2023-03-31 08:18, attributes: ReadOnly).
2026-10-08 11:19:40 [INFO] File deleted.
Confirmation without the extension:
2026-10-08 11:19:39 [ERROR] Confirmation 'example' does not match file name 'example.dll'. Nothing deleted.
Protected location:
2026-10-08 11:19:45 [ERROR] Refusing to delete 'C:\Windows\System32\drivers\etc\hosts': inside protected folder (C:\Windows\System32). Nothing deleted.
File in use:
2026-10-08 11:19:42 [ERROR] Delete error: The process cannot access the file 'C:\ProgramData\ExampleApp\example.log' because it is being used by another process.
2026-10-08 11:19:42 [ERROR] File still exists. It is most likely locked by a running process or service.
Many endpoints
Ask a question with the built-in Tanium Action Log sensor, using the action ID from the top of the log:
Things to know
- Parameters arrive URL-encoded. Tanium passes
C:\ProgramData\ExampleApp\example.dllto the script asC%3A%5CProgramData%5CExampleApp%5Cexample.dll. The script decodes it, and strips extra quotes and spaces, before using it. - It runs as 64-bit. If the Tanium client starts 32-bit PowerShell, the script restarts itself as 64-bit. Without this, a path under
C:\Windows\System32would point toC:\Windows\SysWOW64instead. - There is no Recycle Bin. A file deleted by the package is gone. Run a test action on one endpoint before deploying widely.
- Hidden, system, and read-only files are deleted too.
- A locked file is not retried. If a service holds the file open, stop the service first (for example with another package), or deploy the action again after a reboot.
- An application may recreate the file. If the file belongs to software that is still installed, removing the file alone may only last until the application repairs itself or updates. Uninstall or update the application instead when that is the real fix.
Test results
Each case ran through cmd.exe with URL-encoded parameters. Real deletes ran only against test files created for the purpose. Protected-location cases ran against a copy of the script with the delete command replaced by a stub, so a failed check would have shown up in the log without deleting anything.
| Case | Expected | Result |
|---|---|---|
| Blank path | Exit 2 | Pass |
| Path ending in a backslash | Exit 2 | Pass |
| Wildcard | Exit 2 | Pass |
| Confirmation without the extension | Exit 3, file kept | Pass |
| Read-only, hidden file with a space in the name | Exit 0, file deleted | Pass |
| File already gone | Exit 0 | Pass |
| Folder path instead of a file | Exit 2, folder kept | Pass |
| File held open by another process | Exit 1, file kept | Pass |
| Run from 32-bit PowerShell | Restarts as 64-bit, exit 0 | Pass |
| Symbolic link | Exit 4, link and target kept | Pass |
hosts in System32, a file in SysWOW64, a file in WinSxS |
Exit 4 | Pass |
C:\pagefile.sys, C:\Windows\explorer.exe |
Exit 4 | Pass |
..\ path that resolves into System32 |
Exit 4 | Pass |
| File in the Tanium Client folder | Exit 4 | Pass |
Script
<#
.SYNOPSIS
Deletes a single file on the endpoint. Built for a Tanium package.
.DESCRIPTION
Tanium package command:
cmd.exe /d /c powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File Remove-File.ps1 "$1" "$2"
$1 File Path Full local path of the file to delete, e.g. C:\ProgramData\OldApp\old.dll
$2 Confirm Name Must match the file's name exactly, e.g. old.dll (not case sensitive)
Folders are rejected. Use Remove-Folder.ps1 for folders.
Exit codes:
0 File deleted, or it was already gone
1 Delete failed (usually the file is locked or in use)
2 Invalid or missing file path, or the path is a folder
3 Confirmation did not match the file name, nothing deleted
4 Path is in a protected location or is a symlink, nothing deleted
#>
param (
[string]$FilePath = "",
[string]$ConfirmName = ""
)
# Run as 64-bit so C:\Windows\System32 is not redirected to SysWOW64.
# The raw (still URL-encoded) values are passed on, so they are decoded only once.
if ([Environment]::Is64BitOperatingSystem -and -not [Environment]::Is64BitProcess) {
$ps64 = Join-Path $env:SystemRoot 'Sysnative\WindowsPowerShell\v1.0\powershell.exe'
$argList = @('-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-File', $PSCommandPath)
if ($FilePath) { $argList += @('-FilePath', $FilePath) }
if ($ConfirmName) { $argList += @('-ConfirmName', $ConfirmName) }
& $ps64 @argList
exit $LASTEXITCODE
}
$ErrorActionPreference = 'Stop'
function Write-Log {
param ([string]$Message, [string]$Level = 'INFO')
Write-Output ("{0} [{1}] {2}" -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Level, $Message)
}
# Tanium URL-encodes parameter values. Decode, then strip stray whitespace and quotes.
function ConvertFrom-TaniumParam {
param ([string]$Value)
if ([string]::IsNullOrWhiteSpace($Value)) { return "" }
return ([Uri]::UnescapeDataString($Value)).Trim().Trim('"').Trim()
}
$path = ConvertFrom-TaniumParam $FilePath
$confirm = ConvertFrom-TaniumParam $ConfirmName
Write-Log "Requested file: '$path'"
Write-Log "Confirmation value: '$confirm'"
# --- Validate the path --------------------------------------------------------
if (-not $path) {
Write-Log "File Path is empty. Nothing deleted." 'ERROR'
exit 2
}
if ($path -notmatch '^[A-Za-z]:\\') {
Write-Log "File Path must be a full local path like C:\Folder\file.txt. UNC and relative paths are not allowed." 'ERROR'
exit 2
}
if ($path -match '[\*\?]' -or $path.IndexOfAny([IO.Path]::GetInvalidPathChars()) -ge 0) {
Write-Log "File Path contains wildcards or invalid characters." 'ERROR'
exit 2
}
if ($path.EndsWith('\')) {
Write-Log "File Path ends with a backslash, which points to a folder. Use Remove-Folder.ps1 for folders." 'ERROR'
exit 2
}
try {
# Normalizes ..\ segments and duplicate slashes so the protected-location check can't be bypassed
$full = [IO.Path]::GetFullPath($path)
} catch {
Write-Log "File Path could not be resolved: $($_.Exception.Message)" 'ERROR'
exit 2
}
Write-Log "Resolved file: '$full'"
$parent = Split-Path -Path $full -Parent
$leaf = Split-Path -Path $full -Leaf
# --- Protected locations ------------------------------------------------------
# Any file inside these trees is refused
$protectedTrees = @(@(
(Join-Path $env:SystemRoot 'System32')
(Join-Path $env:SystemRoot 'SysWOW64')
(Join-Path $env:SystemRoot 'SysNative')
(Join-Path $env:SystemRoot 'WinSxS')
(Join-Path $env:SystemRoot 'Boot')
(Join-Path $env:SystemDrive 'Boot')
(Join-Path $env:SystemDrive 'Recovery')
) | Where-Object { $_ } | ForEach-Object { [IO.Path]::GetFullPath($_).TrimEnd('\') } | Select-Object -Unique)
# Files sitting directly in these folders are refused (bootmgr, pagefile.sys, explorer.exe, etc.)
$protectedFolders = @(@(
"$env:SystemDrive\"
$env:SystemRoot
) | Where-Object { $_ } | ForEach-Object { [IO.Path]::GetFullPath($_).TrimEnd('\') } | Select-Object -Unique)
# Tanium Client install folder (64-bit and 32-bit registry locations)
$taniumDir = $null
foreach ($key in 'HKLM:\SOFTWARE\Tanium\Tanium Client', 'HKLM:\SOFTWARE\WOW6432Node\Tanium\Tanium Client') {
try {
$taniumDir = (Get-ItemProperty -Path $key -Name Path -ErrorAction Stop).Path.TrimEnd('\')
break
} catch { }
}
if ($taniumDir) { $protectedTrees += $taniumDir }
$cmp = [StringComparison]::OrdinalIgnoreCase
$parentTrimmed = $parent.TrimEnd('\')
$blockReason = $null
foreach ($t in $protectedTrees) {
if ($full.StartsWith("$t\", $cmp)) { $blockReason = "inside protected folder ($t)"; break }
}
if (-not $blockReason) {
foreach ($f in $protectedFolders) {
if ($parentTrimmed.Equals($f, $cmp)) { $blockReason = "file directly in protected folder ($f\)"; break }
}
}
if ($blockReason) {
Write-Log "Refusing to delete '$full': $blockReason. Nothing deleted." 'ERROR'
exit 4
}
# --- Confirmation (friction layer) --------------------------------------------
if ($confirm -ne $leaf) {
Write-Log "Confirmation '$confirm' does not match file name '$leaf'. Nothing deleted." 'ERROR'
exit 3
}
Write-Log "Confirmation matched file name '$leaf'."
# --- Target checks ------------------------------------------------------------
if (-not (Test-Path -LiteralPath $full)) {
Write-Log "File not found. Nothing to do."
exit 0
}
$item = Get-Item -LiteralPath $full -Force
if ($item.PSIsContainer) {
Write-Log "'$full' is a folder, not a file. Use Remove-Folder.ps1 for folders. Nothing deleted." 'ERROR'
exit 2
}
if ($item.Attributes -band [IO.FileAttributes]::ReparsePoint) {
Write-Log "'$full' is a symbolic link. Refusing to delete." 'ERROR'
exit 4
}
# --- Delete -------------------------------------------------------------------
Write-Log ("Deleting '{0}' ({1:N0} bytes, modified {2:yyyy-MM-dd HH:mm}, attributes: {3})." -f $full, $item.Length, $item.LastWriteTime, $item.Attributes)
try {
# -Force also removes read-only, hidden, and system files
Remove-Item -LiteralPath $full -Force -ErrorAction Stop
} catch {
Write-Log "Delete error: $($_.Exception.Message)" 'ERROR'
}
if (Test-Path -LiteralPath $full) {
Write-Log "File still exists. It is most likely locked by a running process or service." 'ERROR'
exit 1
}
Write-Log "File deleted."
exit 0