Skip to content

Inventory Agent

Ed Mozley edited this page Aug 14, 2026 · 2 revisions

The inventory agent (PowerShell)

A script that runs on a Windows machine, works out what it is, and tells FreeITSM. Hardware, disks, network cards, graphics, TPM, BitLocker, every installed application and every device in Device Manager β€” collected in about thirty seconds and posted straight into the asset record.

Point it at a few machines by hand to see what you get. Put it in a scheduled task and your asset list keeps itself current without anybody typing a serial number.

The script lives at scripts/Invoke-AssetInventory.ps1 in your FreeITSM installation.


Before you start: the API key, and which one

⚠️ The key comes from Software β†’ Settings β†’ API Keys

Not Assets, which is where most people look first, and not System β†’ API, which is a different key system altogether.

This catches people out regularly, so it is worth thirty seconds now rather than a puzzling failure later.

FreeITSM has two unrelated things both called "API keys":

Where What it looks like What it is for
Software β†’ Settings β†’ API Keys 40 characters, hex, no prefix The inventory agent and the other external ingest endpoints. This is the one you want.
System β†’ API starts with fitsm_ The REST API v1, a separate system with granular permissions and Bearer authentication

They are stored separately and are not interchangeable. A key from System β†’ API will never authenticate the inventory script, no matter how valid it is.

The prefix is the tell. If the key you are holding begins fitsm_, it came from the wrong page. The one you need has no prefix at all.

The failure is not obvious when it happens: the script gets all the way through collection, posts, and comes back with Invalid authorization key, which reads like the key is expired or mistyped rather than the wrong kind.

Why Software, when this is about assets?

The same endpoint that receives hardware also receives the installed-application list, which is what feeds the Software module. The key was put where the software inventory settings live and it now serves both. Worth knowing so the location stops feeling arbitrary.

One key per company

On a multi-company install the key decides which company the assets belong to, so generate a separate key per company rather than sharing one.

Running it once, by hand

Start here before automating anything. On the machine you want to inventory:

powershell.exe -ExecutionPolicy Bypass -File "C:\path\to\Invoke-AssetInventory.ps1" -ApiUrl "https://itsm.yourcompany.com" -ApiKey "your-api-key"

You should see it work through the collection, then Success!, then a device count.

-ExecutionPolicy Bypass is not optional. Windows refuses to run downloaded scripts by default β€” without it you get "running scripts is disabled on this system". It applies to that one run and changes nothing on the machine.

Run it as Administrator for the full picture. TPM and BitLocker details need elevation; everything else works as a standard user. In a scheduled task, run as NT AUTHORITY\SYSTEM.

Just look at the data, don't send it

powershell.exe -ExecutionPolicy Bypass -File "C:\path\to\Invoke-AssetInventory.ps1" -OutputFile "C:\Temp\asset.json"

No API key, no network, nothing posted. Useful for seeing exactly what would be sent, and for proving the collection half works when the posting half isn't.


If FreeITSM uses a self-signed certificate

This is the most common reason the agent fails, and it looks alarming:

Could not establish trust relationship for the SSL/TLS secure channel.

Nothing is broken. The machine running the script has been handed a certificate it can't vouch for, so it refuses to send anything β€” which is the correct instinct, because the API key travels inside that connection.

This is normal on an internal address such as https://freeitsm.internal, and it's what you get out of the box on XAMPP, which ships with a self-signed certificate.

⚠️ This is the opposite problem to HTTPS Certificates & CA Bundles. That page is about FreeITSM not trusting Slack or Discord when it makes an outbound call. This is about a client machine not trusting FreeITSM. The fixes are unrelated β€” a CA bundle on the FreeITSM server will not help here.

Three ways through, best first

1. Install the issuing CA certificate into the Trusted Root store on the machines running the script β€” by Group Policy if you have a domain. Nothing about the script changes, and every other tool on those machines benefits too. In a domain with an internal PKI this is usually already true, which is why the problem often shows up on non-domain machines only.

On a default XAMPP install this won't work, and it's worth knowing why before you spend an afternoon on it. XAMPP's certificate is issued to localhost. Even once trusted, it still won't match the address you're actually using, and you'll get a name-mismatch error instead. Either issue a proper certificate for the real hostname, or pin it.

2. Pin the certificate with -CertificateThumbprint. You tell the script exactly which certificate to expect, and it sends the inventory only if the server presents that one:

powershell.exe -ExecutionPolicy Bypass -File "C:\path\to\Invoke-AssetInventory.ps1" -ApiUrl "https://freeitsm.internal" -ApiKey "your-api-key" -CertificateThumbprint "A1B2C3D4E5F60718293A4B5C6D7E8F9012345678"

An impostor server is still refused, so this is safe to leave in place permanently. It deliberately ignores the name on the certificate β€” you've said which exact certificate you mean, so localhost on a default XAMPP certificate is no longer a problem.

3. Skip the check entirely with -SkipCertificateCheck. Every certificate is accepted, including a forged one:

powershell.exe -ExecutionPolicy Bypass -File "C:\path\to\Invoke-AssetInventory.ps1" -ApiUrl "https://freeitsm.internal" -ApiKey "your-api-key" -SkipCertificateCheck

Lab use only. The API key is sent inside that connection, so anyone able to get in the middle β€” rogue DHCP, DNS poisoning, a compromised switch β€” collects a working write credential to your ITSM, plus a full inventory of the machine. You would never know it happened. Use it to prove the rest of the setup works, then switch to a thumbprint.

If both are given, the thumbprint wins and the skip is ignored.

Finding the thumbprint

Whichever method you use, you get 40 characters. Spaces, colons and lower case are all fine β€” the script tidies it up, so paste it however you found it.

Apache, XAMPP, WAMP, nginx β€” the certificate is a file, not in the Windows certificate store. On the FreeITSM server:

(New-Object System.Security.Cryptography.X509Certificates.X509Certificate2("C:\xampp\apache\conf\ssl.crt\server.crt")).Thumbprint

Adjust the path to your certificate. WAMP keeps its own under C:\wamp64\bin\apache\...\conf\, and a certificate you were issued will be wherever you put it.

IIS β€” it is in the certificate store, so on the FreeITSM server:

Get-ChildItem Cert:\LocalMachine\My | Format-List Subject, Thumbprint

A browser, from anywhere β€” visit the FreeITSM address, click through the warning, open the certificate details and look for Thumbprint or SHA-1 fingerprint.

From the client machine β€” the most reliable of the lot, because it reads the certificate that is actually being presented to that machine, whatever the server is running:

$u = [Uri]"https://freeitsm.internal"
$t = New-Object System.Net.Sockets.TcpClient($u.Host, $u.Port)
$s = New-Object System.Net.Security.SslStream($t.GetStream(), $false, { $true })
$s.AuthenticateAsClient($u.Host)
(New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($s.RemoteCertificate)).Thumbprint
$s.Dispose(); $t.Close()

A thumbprint is not a secret. It's a fingerprint of the certificate your server already shows to everyone who connects. It's safe in a script, a GPO or a ticket. The API key is the secret.

When the certificate is renewed, the pin breaks

Renew or regenerate the certificate and the thumbprint changes, so every pinned machine stops posting. That is the pin doing its job, but it looks exactly like a fault β€” and because the agent runs unattended, the first sign is usually an asset list quietly going stale.

The script names the mismatch explicitly when it happens. Read the new thumbprint and update the scheduled task.


Rolling it out

  1. Copy the script to a network share β€” \\server\scripts$\ β€” so every machine can reach one copy.
  2. Create a scheduled task by Group Policy Preferences or your endpoint management tool. Daily or weekly is plenty.
  3. Run as NT AUTHORITY\SYSTEM with highest privileges, for TPM and BitLocker.
  4. Prove it on one machine first, especially the certificate. Every machine will fail the same way, and they fail silently β€” a scheduled task that errors leaves nothing in FreeITSM to notice.

A Group Policy scheduled task action looks like this:

Field Value
Program powershell.exe
Arguments -ExecutionPolicy Bypass -File "\\fileserver\scripts$\Invoke-AssetInventory.ps1" -ApiUrl "https://itsm.yourcompany.com" -ApiKey "abc123"
Run as NT AUTHORITY\SYSTEM
Schedule Daily at 12:00

Add -CertificateThumbprint to the arguments if you need it.

Every run is idempotent. The first contact creates the asset, every run after that updates it, and software that has been uninstalled drops off the inventory. Running it twice does no harm.


What it collects

Group Detail
System Hostname, manufacturer, model, serial, domain, logged-in user, last boot and uptime
OS Edition, feature release (23H2, 24H2), full build number
Processor & memory Name, clock speed, total physical memory
Disks Logical drives with labels and free space, plus physical disk detail
Network Adapters, addressing
Graphics GPU adapters
Security TPM presence and version, BitLocker protection status (both need Administrator)
Devices The full Device Manager tree, grouped by class on the asset screen
Software Installed applications from the registry, split from Windows system components

Where it goes

Two endpoints, both authenticated with the same key:

  • api/external/system-info/submit/ β€” everything above except Device Manager
  • api/external/device-manager/submit/ β€” the Device Manager tree

They're posted separately, and the second one only warns if it fails. You can end up with a complete asset and no device list, so read the whole output rather than just the first Success!.


Troubleshooting

What you see What it means
running scripts is disabled on this system Windows blocking scripts. Add -ExecutionPolicy Bypass.
Could not establish trust relationship for the SSL/TLS secure channel The certificate isn't trusted. See above.
The server did not present the pinned certificate Your -CertificateThumbprint doesn't match β€” usually a renewed certificate.
-CertificateThumbprint should be a 40-character SHA-1 fingerprint Something was lost copying it. The script checks this before collecting, so it fails immediately.
Authorization key missing No -ApiKey was passed at all.
Invalid authorization key Wrong key, or an inactive one. If it starts fitsm_ it is from System β†’ API and will never work here β€” get one from Software β†’ Settings β†’ API Keys instead. See above.
Unknown endpoint: POST /api/external/system-info/submit -ApiUrl has too much on the end. It wants the root of the install, e.g. https://itsm.example.com, not .../api/v1. The script appends the rest itself.
TPM info skipped / BitLocker info skipped Not running as Administrator. Everything else still collected.
Runs fine, nothing in FreeITSM Check the second endpoint's line too, and confirm the key belongs to the company you're looking at.

Windows PowerShell 5.1 is the tricky one. It's what's on a stock Windows machine, and it has no built-in way to accept a self-signed certificate β€” PowerShell 7 does. The agent works around this on 5.1, but if you're testing in PowerShell 7 and deploying to 5.1, test on 5.1.


See also

  • Assets β€” the module the data lands in
  • Software β€” where the installed-application inventory surfaces
  • HTTPS Certificates & CA Bundles β€” the outbound certificate problem, which is a different thing
  • In-app guide: Assets β†’ Help β†’ Inventory script

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Blue sky thinking

Bugs resolved

Links

Clone this wiki locally