# Windows sysinfo client

A Windows PowerShell 5.1 client that sends the same kind of report as the Unix sysinfo client (identity, system, disks, network, installed software) to CAMP as a JSON file once a day. It runs as a scheduled task as SYSTEM. Specification: `Documentation/Spec.md` §7 (in the repository root, not in this directory). It works on Windows 10 and Windows Server 2016 or later; nothing needs installing first.

## What gets installed

| | Path |
|---|---|
| program | `C:\Program Files\camp\sysinfo_client` |
| config, log, unsent report | `C:\ProgramData\camp\sysinfo_client` (`sysinfo_client.json`, `sysinfo_client.log`, `unsent.json`) |
| config editor | `dsEdit.ps1` and `configure.ps1` in the program directory |
| optional host identity file | `C:\ProgramData\camp\server.info` |
| schedule | Task Scheduler, `\camp\sysinfo_client`, daily as SYSTEM, at a random time between 01:00 and 05:00 |

Everything is locked to SYSTEM and Administrators, because the client runs sensors as SYSTEM and refuses to run any that others could change.

## Install (zip, the normal route)

1. Download `sysinfo-client-windows.zip` and `sysinfo-client-windows.zip.sha256` from the release location (`https://example.org/sysinfo_client/` for stable hosts, `https://example.org/sysinfo_client/testing/` for the permanent beta hosts; `example.org` stands for your own release server), and check that the SHA-256 of the zip equals the one in the checksum file. In PowerShell, in the folder you want the files in:

   ```powershell
   $base = 'https://example.org/sysinfo_client'    # add /testing for a beta host
   $ProgressPreference = 'SilentlyContinue'         # without this, Windows PowerShell 5.1 downloads very slowly
   [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12    # only if you get a TLS error
   Invoke-WebRequest -Uri "$base/sysinfo-client-windows.zip" -OutFile .\sysinfo-client-windows.zip
   Invoke-WebRequest -Uri "$base/sysinfo-client-windows.zip.sha256" -OutFile .\sysinfo-client-windows.zip.sha256
   $expected = (Get-Content .\sysinfo-client-windows.zip.sha256 -Raw).Trim().Split()[0].ToLower()
   $actual = (Get-FileHash .\sysinfo-client-windows.zip -Algorithm SHA256).Hash.ToLower()
   if ($actual -eq $expected) { 'checksum OK' } else { 'CHECKSUM MISMATCH - do not install' }
   ```

   `Start-BitsTransfer -Source <url> -Destination <file>` also works and is quicker for large files. If the checksum does not match, download again; if it still does not match, stop and report it.
2. Optional: if a `server.info` is published beside the zip, download it and fill in this host's details (`Owner:`, `Location:` and so on; lines starting with `#` are ignored). In the same folder as the zip (`C:\temp` in this example):

   ```powershell
   Invoke-WebRequest -Uri "$base/server.info" -OutFile C:\temp\server.info
   notepad C:\temp\server.info
   ```

   In the published file each key is listed with no value (`Owner    :`), with the meaning of each in comments above. Type the value after the colon for each one you want (`Owner    : Acme Corp`) and leave the others blank: a key with no value is ignored, so that setting comes from the config file or autodetection as if the line were absent. Save the file and close Notepad. If `$base` is not set (a new PowerShell window), set it again as in step 1. In step 4, add `-ServerInfo C:\temp\server.info` to both install commands. The installer copies the file into the locked `C:\ProgramData\camp` folder and leaves your copy alone, so delete it afterwards if you like. Skip this step when the client name comes from `-ClientName` or the seed.
3. Open PowerShell **as Administrator**. Run `Get-ChildItem .\*.zip | Unblock-File`, then `Expand-Archive .\sysinfo-client-windows.zip -DestinationPath C:\camp-install` (any folder that only administrators can write to).
4. Dry run first, then the real install (with `-ServerInfo C:\temp\server.info` added if you did step 2):

```powershell
cd C:\camp-install\sysinfo_client_windows
powershell -NoProfile -ExecutionPolicy Bypass -File .\install.ps1 -Seed <seed path or URL> -ClientName "<client>" -Location "<location>" -DryRun
powershell -NoProfile -ExecutionPolicy Bypass -File .\install.ps1 -Seed <seed path or URL> -ClientName "<client>" -Location "<location>"
```

The last line must read `RESULT: ok <version> installed`.

- **Seed:** a JSON file of site-wide settings (the upload transport and the log level), the same for every host: `sysinfo-client.seed.example.json` is the model. The transport `URL` must start with `http://` or `https://`. The seed never holds client name, location or tags; they differ per host.
- **`server.info` (optional):** the host identity file (`Owner:`, `Location:`, serial, UUID, tags). Create it anywhere convenient before the install (`C:\temp`, the desktop) and pass `-ServerInfo C:\temp\server.info`, or just drop a file named `server.info` beside `install.ps1`; no other folder is searched. The installer reads it from there, copies it to `C:\ProgramData\camp\server.info` and sets its permissions, so you never create that folder yourself. The source file is left where it is (delete it when you like), and an upgrade run with no such file keeps the installed one.
- **Client name:** the name exactly as CAMP knows it. Give it with `-ClientName`, or put `Owner: <client>` in a `server.info` file, or let the installer ask. If it is still unknown the install finishes with a warning, and the client sends nothing until a name is set.
- **Location** is optional. **Tags** are set with `-Configure` (below) or by editing the config.
- `-Quiet` never prompts. `-DryRun` changes nothing.

## Install from Subversion (backup route)

For hosts that already have a Subversion client. The checkout is code that an administrator runs, so keep it where only administrators can write.

```powershell
svn checkout http://svn.dailydata.net/svn/camp_sysinfo_client_3/stable/windows C:\camp-install\sysinfo_client_windows
cd C:\camp-install\sysinfo_client_windows
powershell -NoProfile -ExecutionPolicy Bypass -File .\install.ps1 -Seed <seed> -ClientName "<client>"
```

Use `testing/windows` instead of `stable/windows` for the beta machines. The branches hold the Windows client in `windows/` and the Unix client in `unix/`, so this checkout contains only Windows files. The installer copies only the client, `lib`, `sensors` and `transports`, so the `.svn` folder and `README.md` are not installed.

## Upgrade

Unpack the new zip (or run `svn update` in the checkout) and run `install.ps1` again, as above. The program directory is replaced, the scheduled time is kept, and an existing config is **never** changed: `-ClientName`, `-Location` and `-Seed` are ignored with a warning. Or let the updater do it (next section).

## Update from the release zip

`sysinfo_update.ps1` is installed beside the client. It downloads the release zip named in the config key `update.url`, checks it against `update.sha256_url` (a missing checksum file only warns; `-RequireChecksum` makes it fatal), unpacks it to a temporary folder, and, only when the version differs, runs that release's `install.ps1 -Quiet`. Nothing is built in: with no URL it stops and tells you which key to set. Run it in an elevated PowerShell:

```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File "C:\Program Files\camp\sysinfo_client\sysinfo_update.ps1"
```

- **How it decides.** It reads the small `.sha256` file first and compares it with `update_installed.sha256` in `C:\ProgramData\camp\sysinfo_client` (written after each successful update, deleted by every `install.ps1` run). Equal means `nochange` and the zip is never downloaded; different or no record means it downloads, verifies and installs, whatever the version number says, so a host updated by hand or never updated by the updater reinstalls once. Only when no checksum is published does it compare version numbers. `-Force` bypasses the comparison.
- `-Check` only reports whether a different version is available (no elevation needed). `-Force` reinstalls the same or an older version. `-Url` and `-Sha256Url` override the config for one run. `-Quiet` prints only the `RESULT:` line.
- It ends with `RESULT: ok|nochange|fail <version> <message>`; exit code 0 (updated), 1 (already current) or 2 (failed). The log is `C:\ProgramData\camp\sysinfo_client\sysinfo_update.log`.
- **Desktop shortcut for a maintenance user.** Create a shortcut whose target is `powershell.exe -NoProfile -ExecutionPolicy Bypass -File "C:\Program Files\camp\sysinfo_client\sysinfo_update.ps1" -Pause`. `-Pause` makes it ask for administrator rights (UAC prompt) and keep the window open until Enter is pressed, so the `RESULT:` line can be read. Do not use `-Pause` in a scheduled task.
- **Setting the URLs.** A new install takes `update` from the seed (see the seed example), or pass `-UpdateUrl` and `-UpdateSha256Url` to `install.ps1`. On a host with an existing config, set them once with `install.ps1 -Configure -UpdateUrl <zip URL> -UpdateSha256Url <checksum URL>` (nothing else is changed or asked).
- **Hosts installed before the updater existed** have no `sysinfo_update.ps1`: run the new release's `install.ps1` once by hand, as above (add the two `-Update...` options as the host's config already exists).
- **Scheduling is not set up for you.** To run it weekly as SYSTEM, for example:

  ```powershell
  schtasks /Create /TN "\camp\sysinfo_update" /SC WEEKLY /D SUN /ST 03:30 /RU SYSTEM /RL HIGHEST /TR "powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File \"C:\Program Files\camp\sysinfo_client\sysinfo_update.ps1\" -Quiet"
  ```

  A run that finds nothing new exits 1, which Task Scheduler shows as the last result; that is not a failure.

## Change the client name, location or tags

Run elevated from the release directory:

```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File .\install.ps1 -Configure
```

It shows the current values and asks for each; Enter keeps a value, `-` clears location or tags, and tags are comma separated. With `-ClientName` or `-Location` on the command line it applies them without asking. The change takes effect at the next run. Editing `sysinfo_client.json` by hand as Administrator also works.

## Edit the config safely (dsEdit)

`configure.ps1` opens the config in a small browse-and-edit screen, so there are no braces or commas to get wrong: the file is only written from a valid structure, after a check that it reads back correctly, and the previous contents are kept in `sysinfo_client.json.bak`. It asks for administrator rights (a UAC prompt) when it needs them.

```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File "C:\Program Files\camp\sysinfo_client\configure.ps1"
```

On the screen, type a number to open an entry (a list or section) or change its value, `a` to add, `d 3` to delete entry 3, `u` to go up, `s` to save and quit, `q` to quit without saving, `?` for the list. Types are kept: a number stays a number, true/false stays true/false. Adding a transport is `a`, a name, `m` for a map, then open it and add its keys.

- **Any file, from a desktop shortcut.** `dsEdit.ps1` edits any `.json` file or INI-style file (`.ini`, `.conf`, `.info`; `server.info` uses `:` as its delimiter), and takes the file as its first parameter. A shortcut with the target `powershell.exe -NoProfile -ExecutionPolicy Bypass -File "C:\Program Files\camp\sysinfo_client\dsEdit.ps1" -Pause "C:\ProgramData\camp\server.info"` opens exactly that file; `-Pause` keeps the window open until Enter so errors can be read. `install.ps1 -DesktopShortcut` creates two shortcuts, "CAMP sysinfo config" (`configure.ps1 -Pause`, the client config) and "CAMP sysinfo update" (`sysinfo_update.ps1 -Pause`), on the desktop of the user running the installer (`-ShortcutAllUsers`: the public desktop). Both are flagged Run as administrator, so double-clicking gives a UAC prompt first; the program directory is locked to SYSTEM and Administrators, so an unelevated user cannot read the scripts and they could not elevate themselves. Any shortcut you make by hand for these scripts needs the same flag (Properties, Shortcut, Advanced, Run as administrator).
- **Scripted changes.** `-Set key=value`, `-Add list=value`, `-Remove key` (or `-Remove list=value`) with dotted paths, for example `configure.ps1 -Set update.url=https://example.com/sysinfo.zip`. Run from `cmd` or a shortcut, each takes one value per run; in a PowerShell session several can be given as `-Set a=1,b=2`. `-Show` prints the result.
- **Mistakes it catches.** A JSON file that is already broken is reported and left alone. For the client config it warns when a value has the wrong type (`tags` not a list, `sensorTimeout` not a number, a transport that is not a section) and asks before writing; a script run refuses unless `-Force`.
- **Limits.** JSON and INI only (no YAML). In an INI file, comments after the first entry and lines without a delimiter are dropped (it tells you how many). The `.bak` file has the same locked permissions as the file.

## Uninstall

```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File .\install.ps1 -Uninstall          # keeps the config
powershell -NoProfile -ExecutionPolicy Bypass -File .\install.ps1 -Uninstall -Purge   # removes it too
```

## Check that it works

```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File 'C:\Program Files\camp\sysinfo_client\sysinfo_client.ps1' -Test
```

`-Test` sends nothing and writes the report to `%TEMP%\sysinfo.testing.json`. To run it for real, as the schedule does: `Start-ScheduledTask -TaskPath '\camp\' -TaskName 'sysinfo_client'`, then look at `Get-ScheduledTaskInfo -TaskPath '\camp\' -TaskName 'sysinfo_client'` (`LastTaskResult` 0 is success) and the end of `C:\ProgramData\camp\sysinfo_client\sysinfo_client.log`.

## Troubleshooting

- **Nothing arrives at CAMP:** read the log. `Report sent` is success. `unsent.json` beside the config means every transport failed; it is deleted by the next successful send.
- **`URL must start with http:// or https://`:** fix `transports.0.URL` in the config.
- **`no clientName defined`:** run `install.ps1 -Configure`, or set `Owner:` in `server.info`.
- **A sensor shows `skipped_permission`:** someone other than SYSTEM or Administrators can write that file or its folder. Run `install.ps1` again, which resets the permissions.
- **Wrong date in the report:** the host's clock is wrong.
- **Log level:** set `logging.log level` in the config (0 errors only, 5 everything), or run with `-LogLevel`.

## Security note

The client does not check the permissions of `C:\ProgramData\camp\server.info`. Whoever can write that file can change the client name, host name, serial number and UUID the host reports. The installer locks `C:\ProgramData\camp` so only administrators can write there; do not loosen it.

## Tests (development only)

The tests are not part of this directory or the zip. They live in `t/windows/` at the repository root and read `windows/` beside them, so run `t\windows\run_tests.ps1` from a checkout or export of the whole repository (trunk), never from the installed directory. It runs under Windows PowerShell 5.1 (all checks, ACL checks need an elevated session) and under `pwsh` on Linux (Windows-only checks are skipped). Sensors are documented in `sensors/README.md`.
