tatersecurity.com Open App

Agent Deployment

Install and configure the TATER compliance agent on Windows, macOS, and Linux endpoints for automated evidence collection, OS compliance scanning, and speed testing.

TATER Agent Settings page showing deployment KPIs, MSI download, and install commands
Before you deploy: review network requirements

The agent makes outbound HTTPS connections to api.tatersecurity.com and www.tatersecurity.com. If your environment uses an SSE/SASE platform (Microsoft Global Secure Access, Zscaler, Netskope, Cisco Umbrella, Palo Alto Prisma) or a corporate proxy with TLS inspection, you almost certainly need to add bypass rules. See Agent Network Requirements for the full domain allowlist and SSE/SASE bypass guide. Most agent-not-reporting cases trace back to TLS inspection breaking the API connection.

If your environment also enforces EDR / AV path or process exclusions, AppLocker / WDAC application allow-listing, or a strict PowerShell execution policy, provide your security team the agent's current SHA-256 hashes (from Settings > Endpoint Agent or the /api/agent/version endpoint), install paths, and service names so they can add the appropriate exclusions.

Agent Overview

The TATER Agent is a lightweight, cross-platform Go binary that runs as a system service on Windows, macOS, and Linux endpoints. It collects compliance data, runs security benchmark checks, and uploads results to the TATER API. Key features:

  • Windows: CIS benchmark scanning (registry, services, firewall, audit policy) via PowerShell controls
  • macOS: 55 security controls (FileVault, Firewall, Gatekeeper/SIP, auto-updates, screen lock, sharing, login window, sudo hardening, APFS encryption, and more) via bash scripts
  • Linux: 60 security controls (firewall, SSH hardening, disk encryption, SELinux/AppArmor, auditd, kernel hardening, and more) via bash scripts
  • Network speed testing using self-hosted test files
  • Automatic result upload to the TATER API
  • Auto-update with SHA-256 hash verification
  • System tray application for status monitoring (Windows and macOS)
  • Local compliance dashboard (opens in browser)
PlatformBinaryServiceTrayControls
Windows (x64)tater-agent.exeWindows SCMYesPowerShell (.ps1)
macOS (Apple Silicon)tater-agent-darwin-arm64launchdYesBash (.sh)
macOS (Intel)tater-agent-darwin-amd64launchdYesBash (.sh)
Linux (x64)tater-agent-linux-amd64systemdNo (headless)Bash (.sh)
Linux (ARM64)tater-agent-linux-arm64systemdNo (headless)Bash (.sh)

MSI Installation

Interactive Installation

Download the MSI

Download TATER-Agent.msi from the TATER Settings → Endpoint Agent page, or from https://www.tatersecurity.com/Agent/TATER-Agent.msi.

Run the installer

Double-click the MSI file. The installation wizard will prompt for the TATER API base URL, API key, and Organization ID.

Enter configuration

Provide the API base URL (e.g., https://api.tatersecurity.com/api), your API key, and your organization ID. The installer auto-appends /api if not present.

Complete installation

Click Install. The agent service starts automatically and the tray application launches.

Silent Installation

For unattended deployments, use msiexec with the /qn flag:

# Basic silent install
msiexec /i TATER-Agent.msi /qn APIBASE="https://api.tatersecurity.com/api" APIKEY="your-key" TENANTID="your-tenant" ORGANIZATIONID="your-org"

# With logging for troubleshooting
msiexec /i TATER-Agent.msi /qn APIBASE="https://api.tatersecurity.com/api" APIKEY="your-key" TENANTID="your-tenant" ORGANIZATIONID="your-org" /l*v "%TEMP%\tater-install.log"

# Uninstall silently
msiexec /x TATER-Agent.msi /qn

MSI Properties

MSI properties use uppercase KEY=value syntax on the msiexec command line. The installer writes these to config.json during installation (see Configuration File Reference below).

PropertyRequiredconfig.json KeyDescription
APIBASEYesapiBaseTATER API base URL. The MSI auto-appends /api if missing.
APIKEYYesapiKeyAPI key for authenticating scan uploads. Generate from Manage → Connections → API Keys.
TENANTIDYestenantIdYour Azure AD tenant ID (GUID).
ORGANIZATIONIDYesorganizationIdOrganization ID (format: org-xxxxxxxx). Found on Settings → Endpoint Agent.
APPIDNoappIdApp registration client ID for app-only cloud scan authentication.
CERTTHUMBPRINTNocertificateThumbprintCertificate thumbprint for app-only authentication. Note: the config.json key is certificateThumbprint - not certThumbprint.
M365ORGNoorganizationYour Microsoft 365 tenant domain (e.g. contoso.onmicrosoft.com). Note: the config.json key is organization - not m365Org.
SCHEDULEFREQUENCYNo(schedule object)Scan schedule frequency: Daily (default), Weekly, or Hourly.
SCHEDULEHOURNo(schedule object)Hour of day to run (0–23, default 3). Only used when SCHEDULEFREQUENCY is Daily or Weekly.
Argument Format Difference: MSI vs Direct Invocation

The MSI installer uses uppercase KEY=value Windows Installer property syntax, while the Go agent binary reads directly from config.json. These use different key names in several cases:

  • CERTTHUMBPRINT (MSI) → certificateThumbprint (config.json) - completely different casing and name
  • M365ORG (MSI) → organization (config.json) - completely different name
  • ORGANIZATIONID (MSI) → organizationId (config.json) - case change only

When writing a config.json directly (macOS, Linux, or custom Windows deploy), always use the camelCase config.json key names, not the MSI property names.

Installation Paths (Windows MSI)

The TATER MSI creates files in two separate directories:

PathContentsNotes
C:\ProgramData\TATER\Agent binary (tater-agent.exe) + config, logs, controlsHardcoded via CommonAppDataFolder\TATER in Directories.wxs. The MSI does NOT install to Program Files.
C:\ProgramData\TATER\All runtime dataAlways this path - not affected by install directory selection.
C:\ProgramData\TATER\config.jsonAgent configurationWritten by MSI from installer properties. Edit to reconfigure without reinstalling.
C:\ProgramData\TATER\Logs\Agent log filesRotated automatically; last 10 logs retained.
C:\ProgramData\TATER\Controls\PowerShell control scriptsDownloaded from TATER at runtime for compliance checks.
C:\ProgramData\TATER\Scans\Local scan result cacheLast 10 scan results retained.

Windows Service: The MSI installs a Windows service named TATERAgent. Detection scripts should query this service directly:

Get-Service -Name "TATERAgent" -ErrorAction SilentlyContinue

Configuration File Reference

The agent reads configuration from config.json at startup. On Windows the MSI writes this file; on macOS and Linux your install script must write it directly.

{
  "apiBase": "https://api.tatersecurity.com/api",
  "apiKey": "tater_xxxxxxxxxxxx",
  "tenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "organizationId": "org-xxxxxxxxxxxxxxxx",

  // Optional: app-only authentication for cloud scanning
  "appId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "certificateThumbprint": "AABBCC...",
  "organization": "contoso.onmicrosoft.com",

  // Optional: agent behavior
  "products": "All",
  "scanIntervalHours": 24,
  "controlsPath": "C:\\ProgramData\\TATER\\Controls"
}
KeyTypeDescription
apiBasestringTATER API base URL. Must end in /api.
apiKeystringAPI key for scan upload authentication.
tenantIdstringAzure AD tenant ID (GUID).
organizationIdstringTATER Organization ID (format: org-xxxxxxxx).
appIdstringApp registration client ID for app-only cloud authentication.
certificateThumbprintstringCertificate thumbprint (hex, uppercase). Used with appId for app-only auth.
organizationstringMicrosoft 365 tenant domain (e.g. contoso.onmicrosoft.com).
productsstringCompliance products to scan. Default: All. Options: All, AllCloud, AllEndpoint.
scanIntervalHoursintegerHours between automated scans. Default: 24.
controlsPathstringDirectory containing control scripts. Windows default: %ProgramData%\TATER\Controls.
logPathstringDirectory for agent log files. Windows default: %ProgramData%\TATER\Logs.
interScriptDelayMsintegerOptional EDR throttle (v2.4.20+). Fixed delay between control-script spawns, in ms. Default 0 (no throttle). See "EDR-sensitive endpoints" callout below.
interScriptJitterMsintegerOptional EDR throttle (v2.4.20+). Additional randomized delay added to each spawn pause (0 to interScriptJitterMs, in ms). Default 0. See "EDR-sensitive endpoints" callout below.
EDR-sensitive endpoints - spawn throttle

The agent spawns one powershell.exe per control script. On endpoints with aggressive EDR behavioral monitoring (SentinelOne, CrowdStrike, Defender for Endpoint in HIPS mode), the sustained spawn cadence can amplify anti-tamper telemetry (Event 143 storms from Service Performance subkey touches at PS startup) and trip behavioral heuristics. ADO #560.

Recommended throttle for those endpoints (add to config.json):

"interScriptDelayMs":  250,
"interScriptJitterMs": 1250

Each control pair now waits 250–1500ms before the next spawn. For a ~1300-control fleet this adds roughly 5–27 minutes per scan vs. unthrottled, with the per-spawn anti-tamper events no longer arriving as a tight burst. The effective values are echoed once at scan start in the agent log when either knob is > 0.

Prerequisites

The TATER Agent supports Windows 10/11 or Windows Server 2019+, macOS 12+ (Apple Silicon or Intel), and Linux (amd64 or arm64, systemd required). The agent service runs as SYSTEM (Windows) or root (Linux/macOS) and requires local administrator privileges for compliance checks (registry, services, firewall, audit policy).

Intune Deployment (Windows)

Two deployment methods are supported via Intune, plus traditional Group Policy / SCCM. Pick based on the host type and what you need from ongoing health monitoring:

MethodBest forInitial installAuto-upgradeSelf-heals driftAVD multi-session
Win32 App (recommended)Standard workstations - the most reliable rollout path in field-tested deploymentsYes (msiexec)Yes (Intune supersedence + agent auto-update)Limited (re-install on detection-rule miss)No (unsupported by Microsoft)
Proactive RemediationAVD multi-session hosts + ongoing health monitoring layer on top of Win32 deploymentYes (fallback when Win32 unsupported)Yes (Remediate's Phase 3b binary swap)Yes (re-runs on schedule, catches dead apiBase / stale versions / broken state)Yes
Group Policy / SCCMOn-prem AD environments without IntuneYesNoNoN/A
Field-tested recommendation

For standard Windows workstations, deploy via Win32 App - it's the most reliable initial-install path in real-world Caron Bletzer rollouts. Optionally layer Proactive Remediation on top (using the Detect-TATERAgent.ps1 + Remediate-TATERAgent.ps1 scripts) to catch post-deployment drift (stale binary, broken config, decommissioned apiBase URL, service stopped, etc).

For AVD / Windows Server multi-session hosts where Win32 App is unsupported, use Proactive Remediation as the primary install path - the Remediate script handles full installation in addition to ongoing health monitoring.

Win32 App (Recommended)

The Win32 App method packages the MSI as an Intune-deployable bundle (.intunewin). It's the most reliable initial-deployment path for standard Windows workstations - field-tested across the CB fleet as significantly more dependable than Proactive Remediation for first-install rollouts.

1. Get the .intunewin package

Two options:

Option A: Download the pre-built package (recommended - saves you from installing the Win32 Content Prep Tool):

iwr https://www.tatersecurity.com/Agent/TATER-Agent.intunewin -UseBasicParsing -OutFile TATER-Agent.intunewin

The package is rebuilt and published to the SWA on every TATER release - it always wraps the current MSI from Installer\bin\Release\TATER-Agent.msi.

Option B: Build locally (only needed if you want to customize the MSI or use a non-current version):

# From the TATER repo root
cd Installer
.\Build-IntuneWin.ps1
# Output: Installer\bin\IntuneWin\TATER-Agent.intunewin

The build script auto-downloads IntuneWinAppUtil.exe on first run (no manual install of the Win32 Content Prep Tool needed).

Verify the download - don't upload a 404 page to Intune

The SWA's incremental sync occasionally leaves /Agent/* URLs returning 404 (~6 KB of HTML) for 5-15 minutes after a TATER release deploy. If you happen to download during that window, iwr -OutFile writes the 6 KB HTML page to your destination file with a .intunewin extension - Intune then rejects it with a confusing "package format" error. Always verify the SHA256 before uploading:

(Get-FileHash TATER-Agent.intunewin -Algorithm SHA256).Hash
# Should match the current published SHA at:
#   https://api.tatersecurity.com/api/agent/version (msiSha256 field is the MSI; the .intunewin wraps it)
# If you get something completely different (or a tiny ~6 KB file), the SWA
# was mid-sync - wait 5-10 min and re-download, or pull from the source
# of truth at:
#   https://github.com/.../TATER/raw/main/Agent/TATER-Agent.intunewin
# (or your local clone at C:\path\to\TATER\Agent\TATER-Agent.intunewin)

Quick file-size sanity check: the .intunewin is ~5.5 MB. If your downloaded file is <100 KB, it's the 404 HTML page - do not upload it.

2. Add the Win32 app in Intune

  1. Open Microsoft Intune admin centerAppsAll appsAdd
  2. App type: Windows app (Win32)
  3. Select app package file: upload TATER-Agent.intunewin
  4. App information page - suggested values:
    • Name: TATER Compliance Agent
    • Description: Continuous compliance scanning, vulnerability inventory, and remote operations agent for TATER Security.
    • Publisher: TATER Security
    • Logo: TATER.png from the repo (or skip)
    • Category: Computer management

3. Install and uninstall commands

Install command - replace the placeholders with your actual values from TATER Manage → Connections → API Keys (key) and the org's properties (tenant + org IDs):

msiexec /i TATER-Agent.msi /qn APIBASE="https://api.tatersecurity.com/api" APIKEY="<your-api-key>" TENANTID="<your-tenant-id>" ORGANIZATIONID="<your-org-id>"

Uninstall command:

msiexec /x TATER-Agent.msi /qn

Install behavior: System. Device restart behavior: No specific action (the agent installs as a Windows service and starts immediately - no reboot needed).

4. Requirements

  • Operating system architecture: x64
  • Minimum operating system: Windows 10 1903 (any supported Windows 10/11/Server 2019+ build works)

5. Detection rule

Use a File rule rather than MSI product code so future agent updates (which happen via in-place binary swap, NOT MSI repair) don't break detection:

  • Rules format: Manually configure detection rules
  • Rule type: File
  • Path: C:\ProgramData\TATER
  • File or folder: tater-agent.exe
  • Detection method: File or folder exists
  • Associated with a 32-bit app on 64-bit clients: No

Why C:\ProgramData\TATER and not C:\Program Files\TATER? The WiX installer roots [TATERDIR] at CommonAppDataFolder\TATER (= %ProgramData%\TATER) so the binary, config, logs, and controls all sit together at a path that Windows services can read and write without elevation gymnastics (see Installer/Directories.wxs line 8). The MSI never places the binary under Program Files. Pointing the Intune detection rule at C:\Program Files\TATER\tater-agent.exe - what older guidance suggested - means Intune sees "file missing" after every successful install and reports the deployment as failed forever, even though the agent is running normally.

(MSI product-code detection works for the very first install but breaks after the agent auto-updates - see ADO #568 for why - so the file-existence rule is more reliable long-term.)

6. Assign and deploy

  1. Assignments tab: assign to the target Entra ID group containing the devices that should run the agent (typically the org's Windows-workstation group).
  2. Recommended assignment type: Required (so Intune installs automatically on next sync) rather than Available (user-initiated).
  3. Review + create.

Devices receive the install on their next Intune sync (typically every 8 hours, or trigger manually via Settings > Accounts > Access work or school > Sync). Install logs land in C:\ProgramData\Microsoft\IntuneManagementExtension\Logs\IntuneManagementExtension.log.

7. Post-install verification (one-liner)

On any newly-deployed device, the field-tested verification script confirms the agent is correctly installed, configured, and reachable. Run from an elevated PowerShell:

Remove-Item $env:TEMP\Verify.ps1 -Force -ErrorAction SilentlyContinue
iwr https://www.tatersecurity.com/Agent/Verify-TATERAgent.ps1 -UseBasicParsing -OutFile $env:TEMP\Verify.ps1
powershell.exe -ExecutionPolicy Bypass -File $env:TEMP\Verify.ps1

Add -Fix at the end to auto-heal anything that's wrong (rewrite dead apiBase, force-upgrade stale binary, restart stopped service, kill orphan tray instances). See Verify-TATERAgent.ps1 reference.

Expected healthy report:

  • [ OK ] Installed version - v2.4.13
  • [ OK ] config: apiBase - https://api.tatersecurity.com/api
  • [ OK ] Service is Running
  • [ OK ] Canonical API reachable
  • Final line: All checks passed. TATER agent is healthy.
Windows PowerShell 5.1 quirks (the default Windows shell)

Most field machines are on Windows PowerShell 5.1, not PowerShell 7. The above one-liner is written for 5.1 compatibility:

  • -UseBasicParsing on Invoke-WebRequest is REQUIRED. Without it, WinPS 5.1 tries the Internet Explorer engine and fails with "Operation cancelled due to security concerns".
  • Use powershell.exe -ExecutionPolicy Bypass -File ... (NOT pwsh.exe - most Windows machines don't have PS 7 installed).
  • An inline iex (iwr ...).Content form ALSO works on WinPS 5.1 today because the SWA serves .ps1 with Content-Type: text/plain. If it ever returns garbled byte-array errors, fall back to the -OutFile path above which works universally.

8. Ongoing upgrades

Two paths handle subsequent version upgrades:

  • Agent auto-update (preferred): v2.4.9+ checks /api/agent/version daily, downloads the new binary, SHA-256 verifies, and replaces in place. Works without Intune intervention. Agents pre-v2.4.6 have a broken auto-update endpoint (see ADO #569) - for those, use the Verify-TATERAgent.ps1 -Fix one-liner OR add the Proactive Remediation health-monitoring layer to force-upgrade them on the next sync.
  • Intune supersedence: when the MSI version itself changes (rare - we only rev the MSI for installer-side changes), supersede the previous Win32 App with the new .intunewin. Intune handles the uninstall-then-install on managed devices.

9. Layer Proactive Remediation for ongoing health monitoring (optional but recommended)

Win32 App handles the install. Proactive Remediation handles drift - stale binaries, broken configs, decommissioned apiBase URLs, stopped services. Set up both for a complete deployment lifecycle. The Proactive Remediation section below describes the scripts in detail.

Proactive Remediation (AVD primary install + health monitoring layer)

Proactive Remediation serves two distinct roles depending on host type:

  • Primary install for AVD multi-session hosts - Win32 App deployment is unsupported on Windows Server multi-session OS (Microsoft limitation). For AVD pools, use Proactive Remediation as the install path. The Remediate script handles full installation when the service is missing.
  • Ongoing health-monitoring layer on top of Win32 App deployment - After Win32 App installs the agent, Proactive Remediation re-runs on a schedule (every 24h by default) to detect drift: stale binary version, broken config, decommissioned apiBase, stopped service, etc. When drift is detected, the Remediate script auto-heals via the same field-verified binary swap pattern used by Verify-TATERAgent.ps1 -Fix.

Whichever role you use it for, the scripts run in SYSTEM context with the same plumbing.

Don't reuse v1.x (PowerShell agent) scripts

If you have older Intune scripts that check for %ProgramData%\TATER\TATER-Agent.ps1 or a scheduled task running PowerShell, those are v1.3.4 (legacy PS agent) patterns. The current v2.x agent is a Go binary (tater-agent.exe) running as a Windows service named TATERAgent. Detection scripts looking for the PS1 file or scheduled task will never find them after a successful install - producing an infinite remediation loop where Intune flags every device as Non-compliant despite the agent working correctly. Use the v2.x scripts below.

Canonical scripts are in the TATER repo

The latest hardened versions of these scripts live as standalone files in Intune/ in the TATER repo:

  • Intune/Detect-TATERAgent.ps1 - v2026.05.25 hardened: placeholder API key detection, writes per-run detect-status.json for forensic collection, BOM-tolerant config parsing
  • Intune/Remediate-TATERAgent.ps1 - v2026.05.25 hardened: rejects placeholder values up front, verifies api.tatersecurity.com reachability before install, force-reinstalls when service exists but config is invalid (broken-state recovery), writes install-status.json on every run, posts a best-effort install beacon to /agent/diagnostics so silent-fleet devices still show up in TATER's Activity Log with via=agent-installer
  • Intune/Collect-TATERStatus.ps1 - one-shot forensic helper to run via Endpoint Central / RDP on any deployed-but-silent device; emits a single JSON blob with service state, binary version + SHA, config (API key redacted), all sentinels, log tail, MSI install log, network reachability. Paste output into the TATER analyst session for triage.

The inline scripts below are kept for copy-paste convenience but lag the repo. Always prefer the standalone files for production rollouts.

Heartbeat in v2.4.11+

Agent v2.4.11+ writes a periodic heartbeat to /api/agent/diagnostics every 30 minutes via a background goroutine (60 s after startup so the initial scan + auto-update can flow first). Before v2.4.11, diagnostics were only written on explicit operator action - so a healthy, scanning, uploading agent could still show Offline on the Devices page once its install-time diagnostic aged past the 90-minute Online window. With v2.4.11, the page receives 3 heartbeats per cycle and stays accurate without intervention. The heartbeat is wired into both service mode and interactive/tray mode (idempotent - Cosmos upsert by deviceName means concurrent processes are safe).

This complements the four health signals in the detection script: the heartbeat ensures the API-side view of the device matches reality, while the detection script ensures the agent process itself is healthy on the host.

Companion fix (2026-05-29): the fleet-list endpoint (GET /api/agent/diagnostics) previously capped its lookup at the 50 most recent reports across the org. With every device emitting 48 heartbeats per day, that window only covered the chattiest ~10–15 devices - so fleets larger than ~20 endpoints silently dropped devices from the Devices / Fleet list. The default is now 1000 reports (configurable via ?limit=N, 1–5000) with a _meta.truncated sentinel for very large estates. See the FAQ entry for diagnostic curl recipe.

Create two PowerShell scripts. The detection script below covers four health signals: service installed, service running, config valid, and recent agent output (so a stuck service that never produces logs gets re-remediated):

Detect-TATERAgent.ps1 - returns exit code 1 (non-compliant) if any health signal fails:

# Detect-TATERAgent.ps1 - Intune Proactive Remediation Detection (Go agent v2.x)
$TATERDir    = Join-Path $env:ProgramData 'TATER'
$ConfigPath  = Join-Path $TATERDir 'config.json'
$LogDir      = Join-Path $TATERDir 'Logs'
$ScansDir    = Join-Path $TATERDir 'Scans'
$MaxAgeHours = 48
$issues      = @()

# 1 - Windows service installed and running
$svc = Get-Service -Name 'TATERAgent' -ErrorAction SilentlyContinue
if (-not $svc) {
    $issues += "TATERAgent Windows service not installed"
} elseif ($svc.Status -ne 'Running') {
    $issues += "TATERAgent service is $($svc.Status), expected Running"
} elseif ($svc.StartType -eq 'Disabled') {
    $issues += "TATERAgent service start type is Disabled"
}

# 2 - config.json present with required keys
if (-not (Test-Path $ConfigPath)) {
    $issues += "config.json not found at $ConfigPath"
} else {
    try {
        $cfg = Get-Content $ConfigPath -Raw | ConvertFrom-Json
        foreach ($key in @('apiBase', 'tenantId', 'organizationId')) {
            if (-not $cfg.$key) { $issues += "config.json missing required key: $key" }
        }
        if (-not $cfg.apiKey -and -not $cfg.encryptedApiKey) {
            $issues += "config.json missing apiKey or encryptedApiKey"
        }
    } catch {
        $issues += "config.json is not valid JSON: $_"
    }
}

# 3 - Recent log or scan evidence (only if service is otherwise healthy)
if ($svc -and $svc.Status -eq 'Running' -and (Test-Path $ConfigPath)) {
    $lastEvidence = $null
    if (Test-Path $LogDir) {
        $latest = Get-ChildItem -Path $LogDir -Filter 'TATER-Agent_*.log' -ErrorAction SilentlyContinue |
            Sort-Object LastWriteTime -Descending | Select-Object -First 1
        if ($latest) { $lastEvidence = $latest.LastWriteTime }
    }
    if (Test-Path $ScansDir) {
        $latest = Get-ChildItem -Path $ScansDir -Filter '*.json' -ErrorAction SilentlyContinue |
            Sort-Object LastWriteTime -Descending | Select-Object -First 1
        if ($latest -and (-not $lastEvidence -or $latest.LastWriteTime -gt $lastEvidence)) {
            $lastEvidence = $latest.LastWriteTime
        }
    }
    if (-not $lastEvidence) {
        $issues += "No agent log or scan files found - service running but agent has not produced output yet"
    } else {
        $ageHours = ((Get-Date) - $lastEvidence).TotalHours
        if ($ageHours -gt $MaxAgeHours) {
            $issues += "Agent last produced output $([math]::Round($ageHours,1))h ago (threshold: ${MaxAgeHours}h)"
        }
    }
}

# 4 - Last-upload sentinel (ADO #535) - catches silent upload failures
# Agent v2.4.0+ writes C:\ProgramData\TATER\last-upload.json after every
# successful POST. If the file exists, treat its timestamp as authoritative
# (it confirms server-side acceptance, not just local activity).
$SentinelPath = Join-Path $TATERDir 'last-upload.json'
if (Test-Path $SentinelPath) {
    try {
        $sentinel  = Get-Content $SentinelPath -Raw | ConvertFrom-Json
        $lastUpload = [DateTime]::Parse($sentinel.timestamp)
        $uploadAge  = ((Get-Date).ToUniversalTime() - $lastUpload).TotalHours
        if ($uploadAge -gt $MaxAgeHours) {
            $issues += "Last successful upload was $([math]::Round($uploadAge,1))h ago (threshold: ${MaxAgeHours}h). Agent is scanning locally but cannot reach the TATER API."
        }
    } catch {
        $issues += "last-upload.json is malformed: $_"
    }
} elseif ($svc -and $svc.Status -eq 'Running') {
    # Sentinel absent on a running v2.4.0+ agent means uploads have never
    # succeeded since install. Pre-2.4.0 agents won't write the file, so we
    # only flag this for agents that we've explicitly confirmed are 2.4.0+.
    $exePath = Join-Path $env:ProgramFiles 'TATER\tater-agent.exe'
    if (Test-Path $exePath) {
        $verInfo = (Get-Item $exePath).VersionInfo
        if ($verInfo.ProductVersion -and $verInfo.ProductVersion -ge '2.4.0') {
            $issues += "Agent v$($verInfo.ProductVersion) has never produced a successful upload (no last-upload.json sentinel). Likely API auth failure - see Manage > Activity Log for via=agent entries."
        }
    }
}

if ($issues.Count -gt 0) {
    Write-Output "NON-COMPLIANT: $($issues -join ' | ')"
    exit 1
}
Write-Output "COMPLIANT: TATERAgent service running, config valid, evidence within ${MaxAgeHours}h"
exit 0

Remediate-TATERAgent.ps1 - installs the MSI, writes config.json directly (overwrites any defaults the MSI dropped), restarts the service, and triggers a one-shot scan:

# Remediate-TATERAgent.ps1 - Intune Proactive Remediation (Go agent v2.x)
# Edit these values before deploying
$Config = @{
    apiBase               = "https://api.tatersecurity.com/api"
    apiKey                = "your-api-key"
    tenantId              = "your-azure-tenant-id"
    organizationId        = "your-org-id"
    products              = "All"
    appId                 = "your-cloud-scan-app-id"             # optional, for cloud scans
    certificateThumbprint = "your-cloud-scan-cert-thumbprint"    # optional, for cloud scans
    organization          = "your-tenant.onmicrosoft.com"        # optional, for cloud scans
    tenantEnvironment     = "commercial"  # commercial | usGovGcc | usGovGccHigh | usGovDoD
}

$TATERDir   = Join-Path $env:ProgramData 'TATER'
$ConfigPath = Join-Path $TATERDir 'config.json'
$LogDir     = Join-Path $TATERDir 'Logs'
$MSI_URL    = "https://www.tatersecurity.com/Agent/TATER-Agent.msi"
$TempDir    = "C:\Windows\Temp\TATERInstall"
$MSIPath    = Join-Path $TempDir 'TATER-Agent.msi'

function Write-Log { param([string]$m) Write-Output "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] $m" }

try {
    # 1. Install MSI if service is missing
    $svc = Get-Service -Name 'TATERAgent' -ErrorAction SilentlyContinue
    if (-not $svc) {
        Write-Log "TATERAgent service not installed - downloading and installing MSI..."
        if (-not (Test-Path $TempDir)) { New-Item -ItemType Directory -Path $TempDir -Force | Out-Null }
        [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
        Invoke-WebRequest -Uri $MSI_URL -OutFile $MSIPath -UseBasicParsing -ErrorAction Stop

        # Pass required MSI properties - the MSI's GenerateConfig custom action will
        # fail the install if APIBASE / APIKEY / TENANTID / ORGANIZATIONID are missing.
        # We still rewrite config.json from $Config in step 3 below, but the MSI itself
        # must succeed first or the service is never installed. Start-Process accepts
        # an array of arguments and quotes each one automatically - no need for
        # PowerShell backtick-escape soup.
        $logPath = Join-Path $TempDir 'tater-install.log'
        $msiArgs = @(
            '/i', $MSIPath,
            '/qn',
            '/l*v', $logPath,
            "APIBASE=$($Config.apiBase)",
            "APIKEY=$($Config.apiKey)",
            "TENANTID=$($Config.tenantId)",
            "ORGANIZATIONID=$($Config.organizationId)"
        )
        $proc = Start-Process -FilePath 'msiexec.exe' -ArgumentList $msiArgs -Wait -PassThru
        if ($proc.ExitCode -ne 0 -and $proc.ExitCode -ne 3010) {
            throw "MSI install failed with exit code $($proc.ExitCode) - see $TempDir\tater-install.log"
        }
        Write-Log "MSI installed (exit code: $($proc.ExitCode))"
    } else {
        Write-Log "TATERAgent service already installed (status: $($svc.Status))"
    }

    # 2. Ensure data dir exists
    foreach ($dir in @($TATERDir, $LogDir)) {
        if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
    }

    # 3. Write config.json (no BOM - Go agent strips BOMs but writing clean is safer)
    Write-Log "Writing config.json..."
    $json = $Config | ConvertTo-Json -Depth 3
    [System.IO.File]::WriteAllText($ConfigPath, $json, [System.Text.UTF8Encoding]::new($false))
    Write-Log "config.json written"

    # 4. Auto-start + (re)start the service so it picks up the new config
    Set-Service -Name 'TATERAgent' -StartupType Automatic -ErrorAction Stop
    $svc = Get-Service -Name 'TATERAgent'
    if ($svc.Status -eq 'Running') {
        Write-Log "Restarting TATERAgent..."
        Restart-Service -Name 'TATERAgent' -Force -ErrorAction Stop
    } else {
        Write-Log "Starting TATERAgent..."
        Start-Service -Name 'TATERAgent' -ErrorAction Stop
    }

    # 5. Do NOT spawn a one-shot scan here (ADO #557)
    #    Earlier versions ran `Start-Process tater-agent.exe -scan` to seed
    #    fresh evidence. The Proactive Remediation script runs in the calling
    #    user's context (often a local admin like JesseMilesAdmin), while the
    #    Windows service starts in SYSTEM context. The two processes then ran
    #    in PARALLEL - two tater-agent.exe instances, different identities,
    #    competing for the same %ProgramData%\TATER files. Confirmed on
    #    X1-01-2025-08 and X1-09-2025-08 in May 2026 (CB tenant).
    #    The freshly-started service performs an initial scan within its
    #    first poll interval (default 60s), which the detection script
    #    picks up on the next Intune sync (default 8h). If you need faster
    #    feedback for testing, RDP into the device and run:
    #        sc.exe stop TATERAgent
    #        sc.exe start TATERAgent
    #    or remotely via TATER Manage → Devices → Run command.

    Write-Log "SUCCESS: TATERAgent remediated"
    exit 0
} catch {
    Write-Log "ERROR: $($_.Exception.Message)"
    exit 1
} finally {
    Remove-Item -Path $MSIPath -Force -ErrorAction SilentlyContinue
}

1. Create the Proactive Remediation

In Intune admin center, go to Devices > Scripts and remediations > Create. Name it "TATER Agent Deployment".

2. Upload scripts

Upload Detect-TATERAgent.ps1 as the detection script and Remediate-TATERAgent.ps1 as the remediation script. Set Run script in 64-bit PowerShell to Yes.

3. Configure schedule

Set Run frequency to Every 1 hour (ensures self-healing). Set Run this script using the logged-on credentials to No (runs as SYSTEM).

4. Assign to groups

Assign to All Windows Devices or a specific device group. For AVD session hosts, use the host pool device group.

Proactive Remediation License Requirement

Proactive Remediation requires Microsoft Intune Plan 1 (included in Microsoft 365 E3/E5, Business Premium, or as a standalone add-on). It is available under Endpoint Analytics in the Intune admin center.

Group Policy / SCCM Deployment

For Group Policy or SCCM deployment, create a deployment package with the MSI and a transform (.mst) file or use command-line properties:

# Group Policy software installation
# Place the MSI on a network share accessible by target machines
# Create a GPO with Software Installation pointing to:
\\fileserver\share\TATER-Agent.msi

# For SCCM, create a package with the following program command line:
msiexec /i TATER-Agent.msi /qn APIBASE="https://api.tatersecurity.com/api" APIKEY="your-key" TENANTID="your-tenant" ORGANIZATIONID="your-org"

macOS Deployment

Platform Scripts (Recommended)

The recommended way to deploy the TATER Agent on macOS via Intune is Platform Scripts (formerly Shell Scripts). A single idempotent script installs the agent, writes the config file, registers the launchd daemon, and starts the service. The script is safe to run multiple times - it skips steps that are already complete.

Don't pass config via command-line flags

The Go agent reads configuration only from /Library/Application Support/TATER/config.json. It does not accept --apiBase, --apiKey, --tenantId, or other config flags - those are silently ignored. If your LaunchDaemon's ProgramArguments array passes 16 string flags to tater-agent, the agent starts with empty config, fails the cfg.ApiBase == "" validation check, and exits. launchctl load still reports success, so the install script appears to succeed, but the agent never actually scans. Also ensure RunAtLoad is true - otherwise the agent only fires at the 3 AM scheduled interval. Use the script below, which writes a proper config.json and invokes tater-agent with no flags.

Strip the macOS quarantine xattr after download

The TATER macOS agent binary is currently not signed or notarized with an Apple Developer ID. Files downloaded via curl automatically receive the com.apple.quarantine extended attribute, which causes macOS Gatekeeper to silently block execution - even when launched by launchd running as root. launchctl load reports success, but the process never starts, so the agent appears installed but never produces logs, scans, or API posts. The binary is also unlaunchable from Finder/Terminal until the xattr is removed. The script below now runs xattr -d com.apple.quarantine after each download. If you've already deployed an earlier version of this script and your devices show installed-but-not-reporting, run sudo xattr -c /usr/local/bin/tater-agent && sudo launchctl kickstart -k system/com.tatersecurity.agent to clear the attribute and restart.

tater_agent_install.sh - create this script and deploy it via Intune:

#!/bin/bash
# tater_agent_install.sh - TATER Agent idempotent installer for Intune Platform Scripts
# Deploy via: Devices > Scripts > macOS > Add

# ── Configure these four values ────────────────────────────────────────────
API_BASE="https://api.tatersecurity.com/api"
API_KEY="your-api-key"
TENANT_ID="your-azure-tenant-id"
ORG_ID="your-org-id"
# ───────────────────────────────────────────────────────────────────────────

set -euo pipefail

ARCH=$(uname -m)
if [[ "$ARCH" == "arm64" ]]; then
    BINARY_URL="https://www.tatersecurity.com/Agent/tater-agent-darwin-arm64"
else
    BINARY_URL="https://www.tatersecurity.com/Agent/tater-agent-darwin-amd64"
fi

INSTALL_DIR="/usr/local/bin"
DATA_DIR="/Library/Application Support/TATER"
PLIST_PATH="/Library/LaunchDaemons/com.tatersecurity.agent.plist"
BINARY_PATH="$INSTALL_DIR/tater-agent"

# Download binary if not already installed or if outdated
if [[ ! -f "$BINARY_PATH" ]]; then
    curl -fsSL "$BINARY_URL" -o "$BINARY_PATH"
    chmod 755 "$BINARY_PATH"
    chown root:wheel "$BINARY_PATH"
    # CRITICAL: strip the com.apple.quarantine xattr that curl applies to downloads.
    # Without this, macOS Gatekeeper silently blocks the unsigned binary from running
    # - launchctl load reports success but the process never actually starts.
    xattr -d com.apple.quarantine "$BINARY_PATH" 2>/dev/null || true
    xattr -c "$BINARY_PATH" 2>/dev/null || true
fi

# Create data directory
mkdir -p "$DATA_DIR/Logs"
mkdir -p "$DATA_DIR/Controls/macOS"

# ADO #566 - Provision macOS control scripts. The agent's runner_darwin.go
# discovers .sh scripts under {controlsPath}/macOS/ at scan time. Without
# these files the agent runs an empty scan and the API returns 400. We
# fetch them from the marketing SWA on every install - idempotent and
# tolerates re-runs (curl overwrites).
CTRL_BASE="https://www.tatersecurity.com/Controls/macOS"
for n in 001_FileVault 002_Firewall 003_GatekeeperSIP 004_AutoUpdates 005_ScreenLock \
         006_RemoteLogin 007_Sharing 008_PasswordPolicy 009_TimeMachine 010_GuestAccount \
         011_AirDrop 012_XProtect 013_NTP 014_Bluetooth 015_SecureBoot \
         016_SoftwareUpdates 017_RemoteManagement 018_LoginWindow 019_Audit 020_NetworkSecurity \
         021_SafariSafeDownloads 022_TerminalSecureEntry 023_ScreenSaverPassword 024_FirewallStealthMode 025_FirewallLogging \
         026_RemoteAppleEvents 027_InternetSharing 028_MediaSharing 029_PrinterSharing 030_NFSExports \
         031_WakeForNetwork 032_PowerNapDisabled 033_FindMyMac 034_iCloudDriveDocSync 035_DiagnosticsSubmission \
         036_SiriEnabled 037_LocationServices 038_AnalyticsSharing 039_PasswordHints 040_FastUserSwitching \
         041_AutomaticLoginOff 042_ConsoleLoginPrompt 043_SSHKeyPermissions 044_SudoTimeout 045_SudoSeparateTty \
         046_LibraryValidation 047_BootROMVersion 048_KernelExtensions 049_SystemIntegrityProtection 050_APFSEncryptedVolumes \
         051_TimeMachineEncrypted 052_CertificateTrustSettings 053_HomeFolderPermissions 054_UmaskSetting 055_GuestAccessSMB; do
  out="$DATA_DIR/Controls/macOS/MAC_${n}.sh"
  curl -fsSL "$CTRL_BASE/MAC_${n}.sh" -o "$out" || { echo "Failed to fetch MAC_${n}"; }
  chmod 755 "$out" 2>/dev/null || true
done
chown -R root:wheel "$DATA_DIR/Controls" 2>/dev/null || true
CTL_COUNT=$(ls -1 "$DATA_DIR/Controls/macOS" 2>/dev/null | grep -c '\.sh$' || echo 0)
echo "Provisioned $CTL_COUNT macOS control scripts"

# Write config (idempotent)
cat > "$DATA_DIR/config.json" <<EOF
{
  "apiBase": "$API_BASE",
  "apiKey": "$API_KEY",
  "tenantId": "$TENANT_ID",
  "organizationId": "$ORG_ID",
  "controlsPath": "$DATA_DIR/Controls",
  "scanIntervalHours": 24
}
EOF

# Register launchd daemon
cat > "$PLIST_PATH" <<PLIST
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key><string>com.tatersecurity.agent</string>
    <key>ProgramArguments</key>
    <array><string>$BINARY_PATH</string></array>
    <key>WorkingDirectory</key><string>$DATA_DIR</string>
    <key>RunAtLoad</key><true/>
    <key>KeepAlive</key><true/>
    <key>StandardOutPath</key><string>$DATA_DIR/Logs/tater-agent.stdout.log</string>
    <key>StandardErrorPath</key><string>$DATA_DIR/Logs/tater-agent.stderr.log</string>
</dict>
</plist>
PLIST

chmod 644 "$PLIST_PATH"
chown root:wheel "$PLIST_PATH"

# Load/reload the daemon
launchctl unload "$PLIST_PATH" 2>/dev/null || true
launchctl load "$PLIST_PATH"

echo "TATER Agent installed and started successfully."
exit 0

1. Upload the script to Intune

In Intune admin center, go to Devices > Scripts > macOS > Add. Upload tater_agent_install.sh.

2. Configure script settings

Set Run script as signed-in user to No (runs as root). Set Hide script notifications on devices to Yes. Script frequency: Not configured (runs once on enrollment; re-run manually as needed).

3. Assign to groups

Assign to All macOS Devices or a specific device group. The script is idempotent - safe to re-run on existing devices.

Manual macOS Installation

# Download and install (Apple Silicon)
sudo TATER_API_BASE="https://api.tatersecurity.com/api" \
     TATER_API_KEY="your-api-key" \
     TATER_TENANT_ID="your-tenant-id" \
     TATER_ORG_ID="your-org-id" \
     bash install.sh

# Check service status
launchctl list com.tatersecurity.agent

# View logs
tail -f "/Library/Application Support/TATER/Logs/tater-agent.stdout.log"

# Restart service
sudo launchctl unload /Library/LaunchDaemons/com.tatersecurity.agent.plist
sudo launchctl load /Library/LaunchDaemons/com.tatersecurity.agent.plist

# Uninstall
sudo bash uninstall.sh

Intune Detection Rule

Set the detection rule to: Custom script that checks test -f /usr/local/bin/tater-agent, or use File exists at /usr/local/bin/tater-agent.

macOS Security Controls (20)

ControlCheckTool
MAC_001FileVault disk encryptionfdesetup status
MAC_002Application Firewall + stealth modesocketfilterfw
MAC_003Gatekeeper + System Integrity Protectionspctl, csrutil
MAC_004Automatic software updatesdefaults read
MAC_005Screen lock password (within 5s)defaults read
MAC_006Remote Login (SSH) disabledsystemsetup
MAC_007No sharing services enabledlaunchctl list
MAC_008Password policy configuredpwpolicy
MAC_009Time Machine backup + encryptiontmutil
MAC_010Guest account disableddefaults read
MAC_011AirDrop not set to Everyonedefaults read
MAC_012XProtect definitions currentXProtect bundle plist
MAC_013NTP time sync enabledsystemsetup
MAC_014Bluetooth not discoverabledefaults read
MAC_015Secure Boot (Full Security)bputil / nvram
MAC_016No pending software updatessoftwareupdate -l
MAC_017Remote Management (ARD) disabledlaunchctl
MAC_018Login window: Name + Passworddefaults read
MAC_019OpenBSM audit daemon runninglaunchctl
MAC_020Wi-Fi using WPA2/WPA3airport -I
MAC_021Safari "Open safe files" disableddefaults read
MAC_022Terminal Secure Keyboard Entrydefaults read
MAC_023Screen saver requires passworddefaults read
MAC_024Firewall stealth mode enabledsocketfilterfw
MAC_025Firewall logging enabledsocketfilterfw
MAC_026Remote Apple Events disabledsystemsetup / launchctl
MAC_027Internet Sharing disableddefaults read (com.apple.nat)
MAC_028Media Sharing disableddefaults read (per user)
MAC_029Printer Sharing disabledcupsctl
MAC_030No NFS exports configured/etc/exports
MAC_031Wake for network access disabledpmset -g
MAC_032Power Nap disabledpmset -g
MAC_033Find My Mac status (informational)nvram
MAC_034iCloud Desktop and Documents syncdefaults read (per user)
MAC_035Diagnostics submission to Apple offDiagnosticMessagesHistory.plist
MAC_036Siri status (review)defaults read (per user)
MAC_037Location Services statuslocationd plist / launchctl
MAC_038Analytics sharing with developers offDiagnosticMessagesHistory.plist
MAC_039Password hints disableddefaults read
MAC_040Fast User Switching disableddefaults read
MAC_041Automatic login disableddefaults read
MAC_042Login window: name + password promptdefaults read
MAC_043SSH host key permissions (600)stat / systemsetup
MAC_044Sudo timeout 5 minutes or less/etc/sudoers
MAC_045Sudo tty_tickets not disabled/etc/sudoers
MAC_046Library Validation not disableddefaults read
MAC_047Firmware version (informational)system_profiler
MAC_048No third-party kernel extensionskextstat
MAC_049SIP + authenticated-root enabledcsrutil
MAC_050APFS Data volumes encrypteddiskutil apfs list
MAC_051Time Machine destinations encryptedtmutil destinationinfo
MAC_052No custom cert trust overridessecurity dump-trust-settings
MAC_053Home folders not group/world readablels -ld
MAC_054No weakened system umasklaunchctl getenv
MAC_055SMB guest access disableddefaults read (com.apple.smb.server)

Linux Deployment

# Download and install (auto-detects amd64/arm64)
sudo TATER_API_BASE="https://api.tatersecurity.com/api" \
     TATER_API_KEY="your-api-key" \
     TATER_TENANT_ID="your-tenant-id" \
     TATER_ORG_ID="your-org-id" \
     bash install.sh

# Check service status
sudo systemctl status TATERAgent

# View logs
sudo journalctl -u TATERAgent -f

# Restart service
sudo systemctl restart TATERAgent

# Uninstall
sudo tater-agent -uninstall
sudo rm /usr/local/bin/tater-agent

Linux installs to /usr/local/bin/tater-agent with config at /var/lib/tater/config.json (root) or ~/.config/tater/config.json (user). The agent registers as a systemd service and runs 60 bash security controls (firewall, SSH hardening, disk encryption, SELinux/AppArmor, auditd, kernel hardening, etc.).

Agent Auto-Update

The TATER Agent automatically checks for updates on each run:

  1. Agent queries GET /api/agent/version (no authentication required)
  2. The API returns the latest version number, download URL, and SHA-256 hash
  3. If a newer version is available, the agent downloads the updated MSI from the TATER SWA
  4. SHA-256 hash is verified against the API-provided hash to ensure integrity
  5. The update is applied silently on the next agent restart
Version Endpoint

The /api/agent/version endpoint requires no authentication, allowing agents behind firewalls to check for updates without API key exposure. The response includes: { version, downloadUrl, sha256 }.

Speed Test

Behind a SSE / SASE proxy?

If your tenant uses Microsoft Global Secure Access, Zscaler, Netskope, Cisco Umbrella, or Palo Alto Prisma Access, the speed-test endpoints below need to be bypassed from traffic forwarding and SSL inspection - otherwise the test measures proxy throughput, not real-world ISP capacity. See the Agent Network Requirements guide for the FQDN bypass list and per-vendor configuration steps.

The agent includes a built-in network speed test that measures download bandwidth:

  • Tests use self-hosted files at https://www.tatersecurity.com/Agent/speedtest/100mb.bin and 50mb.bin
  • No dependency on third-party services (Cloudflare caps at ~25MB)
  • Results are uploaded to the TATER API and visible on the Scans page in the "Speed Test" group
  • Speed test data is also displayed in individual device detail views

Tray Application

The TATER Tray application runs in the system tray and provides:

  • Agent status indicator (running, idle, scanning)
  • Last scan timestamp and result summary
  • Manual scan trigger
  • Speed test trigger
  • Access to agent logs
  • Hidden from the Windows taskbar - only visible in the system tray notification area
  • Uses the TATER shield logo icon

History Cleanup

The agent automatically manages disk space by keeping only the most recent data:

  • Keeps the last 10 log files
  • Keeps the last 10 scan result files
  • Keeps the last 10 speed test result files
  • Older files are deleted at the start of each agent run

Post-install Verification

After deploying to a test device, run these checks to confirm the agent is healthy before rolling out to the full fleet. The first scan typically completes within 10 minutes of install.

Windows

# 1. Service is installed and running
Get-Service -Name 'TATERAgent'
# Expected: Status = Running, StartType = Automatic

# 2. Config file is present and valid JSON
Get-Content "$env:ProgramData\TATER\config.json" | ConvertFrom-Json |
    Format-List apiBase, tenantId, organizationId
# Expected: all three fields populated; no parse errors

# 3. Agent has produced output recently (logs or scans)
Get-ChildItem "$env:ProgramData\TATER\Logs", "$env:ProgramData\TATER\Scans" `
    -Recurse -ErrorAction SilentlyContinue |
    Sort-Object LastWriteTime -Descending | Select-Object -First 5 FullName, LastWriteTime
# Expected: at least one file modified within the last hour after install

# 4. Trigger a one-shot scan if no recent output
& "$env:ProgramFiles\TATER\tater-agent.exe" -scan

macOS

# 1. LaunchDaemon is loaded and process is running
sudo launchctl print system/com.tatersecurity.agent | head -20
# Expected: state = running

# 2. Config file is present
cat "/Library/Application Support/TATER/config.json"
# Expected: apiBase, tenantId, organizationId all populated

# 3. No quarantine xattr blocking the binary (see macOS section above)
xattr /usr/local/bin/tater-agent
# Expected: no output (or no com.apple.quarantine line)

# 4. Recent log output
sudo tail -50 "/Library/Application Support/TATER/Logs/tater-agent.stdout.log"
# Expected: scan loop heartbeat or scan-completion lines

Linux

# 1. systemd service status
sudo systemctl status tater-agent
# Expected: active (running), enabled

# 2. Config file
cat /var/lib/tater/config.json
# Expected: apiBase, tenantId, organizationId populated

# 3. Recent journal output
sudo journalctl -u tater-agent -n 50 --no-pager
# Expected: scan-loop heartbeat or successful upload lines

In TATER (any platform)

  • Open app.tatersecurity.comDevices. The newly installed device should appear within 10 minutes of the first successful scan upload.
  • Open Scans. There should be a fresh endpoint scan with the device's hostname in the list.
  • Open Settings → Endpoint Agent. The status card "Total Devices" and "Last Scan" should reflect the new device.

Troubleshooting

Common Issues

IssueSolution
Intune Proactive Remediation reports Failed but service is installedThe MSI custom action requires APIBASE, APIKEY, TENANTID, ORGANIZATIONID at install time. Older versions of the Remediate script ran msiexec /i $MSIPath /qn with no properties - the install fails before the service is created. Confirm your script passes all four MSI properties on the msiexec command line. See the Remediate-TATERAgent.ps1 above for the correct pattern. Inspect C:\Windows\Temp\TATERInstall\tater-install.log on a failed device to see which property the installer rejected.
Intune detection always reports Non-Compliant after installYou're likely running a v1.x detection script that looks for %ProgramData%\TATER\TATER-Agent.ps1 or a scheduled task. The current v2.x agent is a Go binary running as a Windows service. Replace the detection script with the v2.x version above (checks the TATERAgent Windows service + config file + recent log output).
macOS install succeeds but agent never reports / can't be launched manuallyThe agent binary is unsigned, and curl applies com.apple.quarantine on download - macOS Gatekeeper silently blocks execution even when launchd runs as root. The current install script strips the xattr automatically. To fix already-deployed devices: sudo xattr -c /usr/local/bin/tater-agent && sudo launchctl kickstart -k system/com.tatersecurity.agent.
MSI install fails with BadImageFormatExceptionEnsure the MSI custom actions are compiled as AnyCPU, not x64-only.
Agent installed but no scans appearing in TATERAlmost always one of: (1) outbound HTTPS to api.tatersecurity.com blocked by an SSE/SASE platform doing TLS inspection - see Agent Network Requirements; (2) wrong API key (check config.json); (3) wrong Organization ID (check it starts with org-); (4) the API key was generated in a different organization than the one configured. Check the agent log file for HTTP 401/403 responses to narrow it down.
Agent cannot reach APIVerify HTTPS connectivity to api.tatersecurity.com on port 443. Check proxy settings and TLS inspection rules. SSE/SASE platforms typically need a bypass rule for *.tatersecurity.com.
Scans not uploadingCheck the API key is valid and the Organization ID is correct. Review agent logs in the TATER data directory (%ProgramData%\TATER\Logs on Windows, /Library/Application Support/TATER/Logs on macOS, /var/lib/tater/logs on Linux).
Speed test shows 0 MbpsVerify access to www.tatersecurity.com/Agent/speedtest/. Check for firewall rules blocking large file downloads.
Auto-update not workingThe agent checks /api/agent/version without auth. Ensure this endpoint is accessible. Check SHA-256 hash match.

Integrity Repair

If the agent installation becomes corrupted, run the integrity repair script:

# Run from the TATER installation directory
.\Fix-Integrity.ps1