Deploy ggshield at scale with a service account token
Use this page to deploy Machine Scan on managed endpoints and upload scan inventories to GitGuardian on a schedule. The same pattern works with Jamf Pro, Kandji, Intune, Workspace ONE, Mosyle, Addigy, Canonical Landscape, or configuration management tools such as Ansible, Puppet, Chef, and Salt.
Deployment model
Keep the rollout as three separate MDM jobs. It makes each part easy to audit, repair, and roll back.
| Job | What it does | GitGuardian token | Good MDM fit |
|---|---|---|---|
Install or update ggshield | Deploy the CLI binary | Not needed | package deployment or remediation script |
Install or update machine_scan | Add the Machine Scan plugin for the scan user | Only to install by name | audit/remediation script |
| Inventory scan | Run ggshield machine inventory | Required | scheduled script, once per day |
In MDM terms, an audit script checks whether a machine is compliant; a remediation script installs or repairs what is missing.
Step 1 - Create the token
Create one dedicated Service Account token with the
scan, honeytokens:check, and endpoints:send scopes. One token covers both the
plugin install (when you install by name) and the daily inventory upload.
| Scope | Why it is needed |
|---|---|
scan | Run the scan and install the machine_scan plugin by name |
honeytokens:check | Recognize your honeytokens instead of triggering them |
endpoints:send | Upload the machine inventory to your workspace |
Use a dedicated Service Account token (not a personal token) and do not run
ggshield auth login on fleet machines. If you stage a signed or internal plugin
package instead of installing by name, the install job needs no token at all.
Store the token in your MDM secret store or secret manager, and inject it only at runtime.
endpoints:send is the only scope that writes into your workspace, so it is the
one to contain. The daily upload runs as a single scheduled job, but the
install/update job runs on every machine and is far more exposed. Give the
install/update job a scan-only token and reserve scan, honeytokens:check,
endpoints:send for the upload job: a leaked install token then cannot push forged
inventory or hide findings in your dashboard. The cost is one extra secret to
rotate. If you install the plugin from a staged package rather than by name, the
install job needs no token at all, so a single upload token is already
least-privilege.
Step 2 - Install and manage ggshield
Use the deployment method your fleet already trusts:
- Jamf Pro / 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/.rpmor run the official installer, then enforce the version.
For script-based macOS/Linux deployments, install only the binary first:
curl -sSfL https://raw.githubusercontent.com/GitGuardian/ggshield/main/scripts/install/install.sh | bash -s -- --install-only
Audit with:
ggshield --version # must be >= 1.51.0 to install machine_scan by name
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.
Step 3 - Install Machine Scan for the scan user
Machine scanning must be installed and enabled before running
ggshield machine inventory. The plugin is user-scoped, so install it for the
user that will run the scan (usually the logged-in user on workstations, or a
dedicated target user on servers).
If your MDM can stage a signed/internal plugin package, install that package and no GitGuardian token is needed for the install job:
sudo -i -u "$TARGET_USER" -- ggshield plugin install /path/to/machine-scan-plugin.whl
If you install by name, ggshield plugin install machine_scan requires
ggshield 1.51.0 or later and contacts the GitGuardian plugin catalog. Inject
the service account token at runtime; do not store it with
ggshield auth login on the endpoint. For EU or self-hosted instances, export
GITGUARDIAN_INSTANCE in the same shell.
macOS example for Jamf/Kandji-style scripts:
loggedInUser=$(scutil <<< "show State:/Users/ConsoleUser" | awk '/Name :/ && !/loginwindow/ {print $3}')
# Your MDM injects the service account token 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
Linux example:
# Your MDM injects the service account token as GGSHIELD_SAT.
printf '%s\n' "$GGSHIELD_SAT" | sudo -i -u "$TARGET_USER" -- /bin/bash -c \
'IFS= read -r GITGUARDIAN_API_KEY && export GITGUARDIAN_API_KEY && exec ggshield plugin install machine_scan < /dev/null'
sudo -i -u "$TARGET_USER" -- ggshield plugin list
Audit success when ggshield plugin list shows machine_scan for the scan user.
Step 4 - Run the daily inventory scan
Schedule one MDM job per day that injects the service account token at runtime
and runs ggshield machine inventory as the scan user.
# Your MDM injects the service account token 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 machine inventory < /dev/null'
For Linux, use /bin/bash and your target user. For EU or self-hosted instances,
also export GITGUARDIAN_INSTANCE inside the same shell.
This pattern keeps the token out of command-line arguments, files, logs, and
ggshield auth login. That matters because supply-chain malware commonly scrapes
workstations for credentials in files, shell environments, and package-manager
configs. Disable shell tracing (set -x) in scripts that handle the token.
If your MDM cannot run scheduled scripts reliably, you can use launchd or a
systemd timer as a fallback. Keep the token in a root-only file and remove it on
uninstall.
Step 5 - Roll out and verify
Start with 10–20 monitored machines, then expand in waves (for example 1% → 10% → 25% → 50% → 100%). Spread first scans over hours, not minutes.
On a sample machine:
ggshield --version
sudo -i -u "$TARGET_USER" -- ggshield plugin list
Then trigger the MDM inventory job once. Go to Endpoint protection → Endpoints in GitGuardian and confirm the machine appears with a recent scan.
Monitor only a few signals: install success, plugin enabled, last scan time, scan duration, upload errors, and user performance feedback. To roll back, disable the scheduled inventory scan first.
For a detailed pilot checklist, see Validate and roll out.