# rb — runbooks
Generic, reusable IT procedures and scripts for MSP field work. Fetched onto
client workstations during on-site work with short, hand-typeable commands.
> [!WARNING]
> **This repository is PUBLIC-READ.** It must never contain client-identifying
> information — no client names, hostnames, IPs, usernames, credentials, or
> screenshots. Procedures with placeholders **only**. See
> [CONTRIBUTING.md](CONTRIBUTING.md) for the sanitization rule.
> For qualified IT professionals, on systems they are authorized to administer.
> Provided as-is, without warranty — verify anything here fits your environment
> before running it. Scripts execute in your own session via `iex`; read them
> first. Licensed under [MIT](LICENSE). Contains no client-identifying data by
> policy.
## Using a runbook
Fetch and read on the target workstation:
```powershell
irm rb.godwinsystems.com/od/db-backup.md | more
```
Run an executable runbook script directly (scripts live under `scripts/`):
```powershell
irm rb.godwinsystems.com/scripts/od-db-backup.ps1 | iex
```
Scripts prompt for anything client-specific via `Read-Host` — nothing to edit
before running. See [`scripts/_template.ps1`](scripts/_template.ps1) for the
convention.
No scheme is needed: PowerShell defaults a scheme-less URI to `http://`, and
`rb.godwinsystems.com` answers on :80 with a 301 to HTTPS.
### About that hostname
`rb.godwinsystems.com` is a 302 redirect to this repo's raw path on Gitea. It
exists because these commands are hand-typed on client machines, often
mid-incident — the canonical URL is 63 characters before the filename. The
canonical form still works and is what the redirect targets:
```
https://gitea.ivangodwin.com/godwinsystems/rb/raw/branch/main/
/.md
```
The redirect is a backendless Gateway API `HTTPRoute` defined in
`kubernetes-gitops` at `manifests/apps/rb-redirect/`, with a listener on the
public gateway and a DNS-only A record for `rb`. **If that route is ever
removed, update the commands above in the same change** — otherwise every
runbook here documents a dead URL.
## Layout & naming
```
od/ Open Dental
sec/ Security / incident response
scripts/ Executable .ps1 — one flat namespace, prefixed by domain
```
- **Runbooks** (`.md`) live in a domain directory, named without a prefix —
the directory is the prefix. `od/db-backup.md`, not `od-db-backup.md`.
- **Scripts** (`.ps1`) stay flat in [`scripts/`](scripts/) and keep their
domain prefix. One directory holds everything executable, which is the set
worth auditing before a change; `_template.ps1` sorts first as the
convention reference, not a runnable runbook.
- **Meta** (`README.md`, `CONTRIBUTING.md`, `LICENSE`) stays at the root.
The split is deliberate: the fetch command's length is the constraint this
repo is organized around, and `od/db-backup.md` is exactly as long to type as
`od-db-backup.md` was — the hyphen became a slash. Nesting scripts by domain
too would add characters without adding clarity to a five-file directory.
Domain directories, as they are needed:
| Directory | Domain |
|---|---|
| `win/` | Windows workstation / server |
| `m365/` | Microsoft 365 / Entra |
| `od/` | Open Dental |
| `net/` | Networking |
| `sec/` | Security / incident response |
Non-interactive scripts (Intune remediations) get their own sibling directory
when they arrive — not `scripts/`, which is for `iex`-safe interactive ones.
## Placeholder conventions
Fill these from the private tier (private repo or Bitwarden secure note) at
run time — never commit filled-in values.
| Placeholder | Meaning |
|---|---|
| `` | Client / site identifier |
| `` | Server hostname |
| `` | Share name |
| `` | Local account used for share access |
| `` | End-user account |
| `` | From password manager — never written to a file |
## Contents
### Open Dental — `od/`
| File | Purpose |
|---|---|
| [`od/smb-cred.md`](od/smb-cred.md) | Open Dental SMB share — stored-credential fix |
| [`od/cfg-persist.md`](od/cfg-persist.md) | Open Dental — persist "Do not show this window on startup" (writable FreeDentalConfig.xml) |
| [`od/scan-duplex.md`](od/scan-duplex.md) | Open Dental — duplex ADF scanner captures only one side (TWAIN, Show TWAIN UI branches) |
| [`od/db-backup.md`](od/db-backup.md) | Open Dental — rock-solid cold backup of the database + images (stop/copy/start MySQL/MariaDB) |
| [`od/backup-verify.md`](od/backup-verify.md) | Open Dental — verify a backup by test-restoring into an isolated Hyper-V VM (health checklist) |
| [`od/backup-schedule.md`](od/backup-schedule.md) | Open Dental — schedule the backup + off-site upload and monitor it (dead-man's-switch heartbeat) |
### Security / incident response — `sec/`
| File | Purpose |
|---|---|
| [`sec/google-compromise.md`](sec/google-compromise.md) | Incident response — suspected compromise of a **consumer** Google/Gmail account (AiTM session theft; no Workspace admin console) |
| [`sec/google-evidence.md`](sec/google-evidence.md) | Google/Gmail account — evidence capture before changes + re-entry check after a password reset (numbered, field-usable) |
### Scripts — `scripts/`
| File | Purpose |
|---|---|
| [`scripts/cg-disable.ps1`](scripts/cg-disable.ps1) | Disable Credential Guard, then reboot (prompts to confirm) |
| [`scripts/od-cfg-acl.ps1`](scripts/od-cfg-acl.ps1) | Grant Users Modify on FreeDentalConfig.xml (Option B of od-cfg-persist) |
| [`scripts/od-db-backup.ps1`](scripts/od-db-backup.ps1) | Cold backup: stop MySQL/MariaDB, copy whole data dir + OpenDentImages, always restart (od-db-backup) |
| [`scripts/od-backup-check.ps1`](scripts/od-backup-check.ps1) | Read-only backup health check: freshness/completeness/size + heartbeat ping (od-backup-schedule) |
## Tiers
- **This repo (public):** generic procedures, placeholders only.
- **Private tier:** filled-in, client-specific versions — private repo or
Bitwarden secure notes. Never here.
This repo also serves as the raw source for Intune remediation scripts and as
documented-procedures evidence for E&O / cyber insurance.