Skip to main content

Deploy ggshield at scale with a service account token

Use this page to deploy Developer Endpoint Protection on managed endpoints: machine scanning, honeytoken protection, AI Hooks, and an inventory of the AI agents and MCP servers in use. The same pattern works with Jamf Pro, Iru (formerly Kandji), Intune, Workspace ONE, Mosyle, Addigy, Canonical Landscape, or configuration management tools such as Ansible, Puppet, Chef, and Salt.

We recommend storing no GitGuardian token on the endpoint. Inject an admin service account token at runtime, only for the jobs your MDM runs: install ggshield, install the machine scanning plugin, plant the honeytoken, and running the scans.

AI and git hooks are the exception. They fire in the developer's own session, not in your MDM job, so that machine needs a developer login. Log the developer in and the AI inventory is attributed to them too.

Deployment model

Deploy each capability as its own MDM job so you can audit, repair, or roll it back independently. The capabilities are optional and independent of each other, so you can scan a machine without planting a honeytoken, or install AI Hooks without scanning. Yet, we strongly recommend enabling all of them.

JobWhat it doesGitGuardian tokenGood MDM fit
Install or update ggshieldDeploy the CLI binaryNot neededpackage deployment or remediation script
Install or update machine_scanAdd the Machine Scan plugin for the scan userAdmin SAT, injected at runtimeaudit/remediation script
Machine setupRun ggshield machine setup to configure AI Hooks, git hooks, and the honeytokenAdmin SAT, injected at runtimescheduled script, daily
Developer loginggshield auth logout --no-revoke, then ggshield auth login as the logged-in userDeveloper personal tokenremediation script, once per machine
Inventory scanRun ggshield machine reportAdmin SAT, injected at runtime, or the developer tokenscheduled script, once per day
AI and MCP inventoryRun ggshield ai discoverDeveloper tokenscheduled script

In MDM terms, an audit script checks whether a machine is compliant, and a remediation script installs or repairs what is missing.

Do not persist the admin SAT with ggshield auth login --method token. That overwrites the developer's login, so every later ggshield command on that machine reports as the shared account, and an admin token sits on disk.

Step 1 - Create the admin token

Create one dedicated Service Account token for the MDM jobs. Add only the scopes those jobs need:

ScopeWhy it is needed
scanInstall the machine_scan plugin by name, which downloads it from the GitGuardian catalog
endpoints:sendUpload scan results, when the MDM job runs ggshield machine report itself
honeytokens:writePlant honeytokens on the machine. Only needed for honeytoken protection
honeytokens:checkRecognize your own honeytokens during a scan and avoid reporting it as a regular secret. Only needed for machine scans

Store the token in your MDM secret store or secret manager and inject it at runtime.

Step 2 - Install and manage ggshield

Use the deployment method your fleet already trusts:

  • Jamf Pro / Iru (formerly Kandji) / Mosyle / Addigy: deploy the signed macOS package or run an install remediation script.
  • Intune: deploy the Windows installer or run the PowerShell installer. For macOS/Linux, use shell scripts or remediations.
  • Workspace ONE / RMM tools: use a managed package or script assignment.
  • Landscape / Ansible / Puppet / Chef / Salt: install the .deb/.rpm or run the official installer, then enforce the version.

For script-based macOS/Linux deployments, install only the binary first, and set GGSHIELD_BIN_DIR to a directory already on everyone's PATH:

curl -sSfL https://raw.githubusercontent.com/GitGuardian/ggshield/main/scripts/install/install.sh | \
GGSHIELD_BIN_DIR=/usr/local/bin bash -s -- --install-only

Audit with ggshield --version, which must report 1.53.0 or later for the commands on this page.

For large fleets, prefer an internal package cache or MDM-hosted package so every machine does not download from the internet at the same time.

The examples below reuse this loggedInUser lookup. On Linux, or on a server with a dedicated scan user, use that user instead.

loggedInUser=$(scutil <<< "show State:/Users/ConsoleUser" | awk '/Name :/ && !/loginwindow/ {print $3}')

Step 3 - Install Machine Scan for the scan user

Machine scan plugin must be installed and enabled before running ggshield machine report. Install it for the user that will run the scan. Run the job as root instead and the plugin is installed machine-wide so every user can load it, which is the better fit when several people share a host.

If your MDM can stage a signed or internal plugin package, install that package and the job needs no token:

sudo -i -u "$loggedInUser" -- ggshield plugin install /path/to/machine-scan-plugin.whl

If you install by name, ggshield plugin install machine_scan contacts the GitGuardian plugin catalog. Pipe the admin SAT into GITGUARDIAN_API_KEY through stdin so it stays out of command lines and shell history. Disable shell tracing (set -x) in scripts that handle it.

tip

The token still lives in the process environment, where ps -E on macOS and /proc/<pid>/environ as root on Linux can read it. Configure your MDM to redact secret environment variables and captured stdin from its own logs.

# Your MDM injects the admin SAT as GGSHIELD_SAT.
printf '%s\n' "$GGSHIELD_SAT" | sudo -i -u "$loggedInUser" -- /bin/zsh -c \
'IFS= read -r GITGUARDIAN_API_KEY && export GITGUARDIAN_API_KEY && exec ggshield plugin install machine_scan < /dev/null'

sudo -i -u "$loggedInUser" -- ggshield plugin list

For Linux, use /bin/bash and your target user.

auth login and later ggshield commands default to US SaaS. For EU or self-hosted instances, pass GITGUARDIAN_INSTANCE into the inner shell. The instance URL is not a secret:

export GITGUARDIAN_INSTANCE=https://dashboard.eu1.gitguardian.com # or your self-hosted instance URL

printf '%s\n' "$GGSHIELD_SAT" | sudo -i -u "$loggedInUser" -- \
env GITGUARDIAN_INSTANCE="$GITGUARDIAN_INSTANCE" /bin/zsh -c \
'IFS= read -r GITGUARDIAN_API_KEY && export GITGUARDIAN_API_KEY && exec ggshield plugin install machine_scan < /dev/null'

Use that same env GITGUARDIAN_INSTANCE=... prefix on every later admin job.

Audit success when ggshield plugin list shows machine_scan for the scan user.

Step 4 - Set up hooks and honeytokens

ggshield machine setup configures the 3 endpoint protections in one idempotent run. Drop any of them with --no-ai-hooks, --no-git-hooks, or --no-honeytokens. Run it as the logged-in user, and inject the admin SAT the same way as in Step 3:

printf '%s\n' "$GGSHIELD_SAT" | sudo -i -u "$loggedInUser" -- /bin/zsh -c \
'IFS= read -r GITGUARDIAN_API_KEY && export GITGUARDIAN_API_KEY && exec ggshield machine setup < /dev/null'

Schedule this job daily. It adds missing protections and leaves ones already in place:

  • Writes hooks for AI agents that are not yet protected, and git hooks when they are not yet enforced.
  • Reconciles honeytoken protection against GitGuardian on every run: it rewrites a deleted honeytoken and removes one you revoked in the dashboard. That is why planting stays an MDM job with honeytokens:write.

To pick specific assistants, use --agent. To skip some, use --exclude-agent. Both accept claude-code, codex, copilot, cursor, vibe, and vscode.

Honeytoken

Planting writes a decoy AWS profile in ~/.aws. The process user decides whose home that is:

  • As the logged-in user: that account only, which is enough on a one-person laptop.
  • As root: every user home it finds (/Users, /home, and similar), so every account on a shared host gets a decoy.

On a shared host, run the honeytoken part as root and inject the SAT through stdin:

printf '%s\n' "$GGSHIELD_SAT" | sudo /bin/sh -c \
'IFS= read -r GITGUARDIAN_API_KEY && export GITGUARDIAN_API_KEY && exec ggshield machine setup --no-ai-hooks --no-git-hooks < /dev/null'

Preview targets without writing anything: ggshield honeytoken plant --list-targets. See Protect endpoints with Honeytokens.

AI Hooks

Hooks are written in the current user's AI agent config (~/.claude, ~/.cursor, and the other supported tools). machine setup configures every AI agent it detects for that account.

The hook later runs in the developer's session. It reads that user's credential store, which is why Step 5 logs them in as themselves. If they are not logged in, the hook fails open: it does not block, and the tool shows a warning that the content was not scanned.

On a shared host, keep AI Hooks in the logged-in user job and skip them in the root job (--no-ai-hooks). See Prevent leaks with AI Hooks.

Git hooks

Installs global pre-commit and pre-push hooks. Scope follows the process user:

  • Root, or --system: git's system core.hooksPath, covering every user on the machine.
  • Not root, no --system: git's global core.hooksPath for the current user.

A core.hooksPath that points somewhere else (Husky, lefthook) takes precedence, and the ggshield hook never fires there. A global install is the exception: when a global core.hooksPath already exists, ggshield writes its hook inside that directory instead of being shadowed by it.

ggshield machine doctor reports this per repository, and it resolves the effective path from the current directory. Run your MDM audit script inside a repository, otherwise the check means nothing.

On a shared host, install system hooks as root and skip git hooks in the user job, so you do not set a user-global core.hooksPath that shadows system for that account. System git hooks do not need a token:

sudo ggshield machine setup --system --no-ai-hooks --no-honeytokens

Step 5 - Log the developer in

After the admin jobs, log the developer in so AI Hooks, git hooks, and the AI inventory run under their membership. ggshield auth login requests scan, honeytokens:check, endpoints:send, and ai-discover:send automatically.

auth login keeps an existing token if one is already stored, so log out first. Use --no-revoke so logout only clears the stored token.

sudo -i -u "$loggedInUser" -- ggshield auth logout --no-revoke
sudo -i -u "$loggedInUser" -- ggshield auth login

The login command opens a browser in the developer's session and provisions a personal token. For EU or self-hosted instances, pass --instance on both commands:

sudo -i -u "$loggedInUser" -- ggshield auth logout --no-revoke --instance https://dashboard.eu1.gitguardian.com
sudo -i -u "$loggedInUser" -- ggshield auth login --instance https://dashboard.eu1.gitguardian.com

US SaaS needs no --instance flag.

Step 6 - Run the daily inventory scan

Schedule one MDM job per day that runs ggshield machine report. It scans and sends in one step, see Commands.

Inject the admin SAT for this job, the same way as in Step 3. The scan does not depend on the developer login, which is simpler across a fleet:

printf '%s\n' "$GGSHIELD_SAT" | sudo -i -u "$loggedInUser" -- /bin/zsh -c \
'IFS= read -r GITGUARDIAN_API_KEY && export GITGUARDIAN_API_KEY && exec ggshield machine report < /dev/null'

This job needs endpoints:send only. Detection runs locally, and the plugin checks that scope before it uploads. scan is for downloading the plugin from the catalog, not for scanning the machine.

If you want each scan attributed to the developer instead, drop the injection and run it as the logged-in user after Step 5, which uses their token:

sudo -i -u "$loggedInUser" -- ggshield machine report

For EU or self-hosted instances, pass the instance through env, the same way as in Step 3. Login does not store it for later commands.

If your MDM cannot run scheduled scripts reliably, you can use launchd or a systemd timer as a fallback.

Step 7 - Collect the AI and MCP inventory

ggshield ai discover parses the configuration files of supported AI assistants and reports which agents and MCP servers are present on the machine. This is its own job, separate from the machine scan, so schedule it separately. Run it as the logged-in user after Step 5, so it uses their token and the inventory is attributed to them. AI tooling changes less often than scan results, so a daily or weekly pass is enough.

sudo -i -u "$loggedInUser" -- ggshield ai discover

For EU or self-hosted instances, pass the instance through env, the same way as in Step 3.

For what the inventory collects and how security teams use it, see AI agents and MCP inventory.

Step 8 - Roll out and verify

Start with 10 to 20 monitored machines, then expand in waves. On a sample machine, run the readiness check as the logged-in user, from inside a git repository so the git hooks check resolves the effective core.hooksPath:

sudo -i -u "$loggedInUser" -- /bin/zsh -c 'cd /path/to/a/repo && exec ggshield machine doctor'

machine doctor is read-only and exits non-zero when any check fails, so you can use it directly as an MDM audit script and gate the next wave on it. Every failed check prints its own fix. After Step 5, it should report that the developer token reaches GitGuardian with the scopes the protections need.

Then trigger the MDM inventory job once. Go to Endpoint protection → Endpoints in GitGuardian and confirm the machine appears with a recent scan, and reports as Protected if you deployed honeytoken protection. See Monitor coverage.

To roll back, disable the scheduled jobs first, especially machine setup. Then clear what is already on disk. ggshield auth logout as the logged-in user removes their personal token. Revoke the admin SAT in your workspace to cover the whole fleet at once.

For the pilot checklist, the wave sizes, and what to monitor at each gate, see Validate and roll out.