Initial runbook repo: Open Dental SMB credential fix + conventions

- README: purpose, hand-typeable fetch usage, naming + placeholder conventions
- CONTRIBUTING: public-repo sanitization rule (procedures only, no particulars)
- _template.ps1: iex-safe script convention (Read-Host, no param, admin check)
- od-smb-cred.md: Open Dental SMB stored-credential fix
- cg-disable.ps1: standalone Credential Guard disable + reboot

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-07 11:54:50 -07:00
commit 314f6cf7f8
6 changed files with 377 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
.DS_Store
+53
View File
@@ -0,0 +1,53 @@
# Contributing (note-to-self)
Solo-maintained. This file exists to keep future-me honest.
## The rule
**Procedures only. No particulars.**
This repo is public-read. Anything that identifies a client or would let a
reader act against a client environment goes to the private tier — **no
exceptions.**
Never commit:
- Client / business names, site identifiers
- Hostnames, IPs, subnets, SSIDs, MAC addresses
- Usernames, account names, email addresses
- Passwords, keys, tokens, connection strings, license keys
- Screenshots, exports, logs, or config dumps containing any of the above
Instead use placeholders: `<CLIENT>`, `<SERVER>`, `<SHARE>`, `<SHARE_USER>`,
`<USER>`, `<PASSWORD>`. Filled-in versions live in the **private tier**
(private repo or Bitwarden secure note).
## Where things go
| Content | Home |
|---|---|
| Generic procedure with placeholders | **This repo** |
| Anything needing a credential | Private tier |
| Client-specific config / values | Private tier |
| Any identifying detail | Private tier |
If a step can't be written without a real particular, it doesn't belong here —
split the particular out to the private tier and reference it as a placeholder.
## Before every commit
1. Re-read the diff. Would a stranger learn *who* the client is, or *how to
reach* their systems? If yes, stop.
2. No real hostnames/IPs/users/passwords — placeholders only.
3. No screenshots or pasted output with real data.
4. Scripts prompt for client-specifics at run time; they don't hard-code them.
## Scripts
Follow [`_template.ps1`](_template.ps1):
- Prompt for placeholders with `Read-Host` — no editing before running, no
`param()` (can't pass args through `irm | iex`).
- Safe to run via `irm <url> | iex` from our own server.
- Confirm before anything destructive or that reboots.
- Check for admin explicitly (`#Requires` is not enforced under `iex`).
+70
View File
@@ -0,0 +1,70 @@
# 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.
## Using a runbook
Fetch and read on the target workstation:
```powershell
irm https://gitea.ivangodwin.com/ops/rb/raw/branch/main/<file> | more
```
Run an executable runbook script directly:
```powershell
irm https://gitea.ivangodwin.com/ops/rb/raw/branch/main/<file>.ps1 | iex
```
Scripts prompt for anything client-specific via `Read-Host` — nothing to edit
before running. See [`_template.ps1`](_template.ps1) for the convention.
## Naming
Flat repo, short filenames, light category prefixes so URLs stay
hand-typeable:
| Prefix | Domain |
|---|---|
| `win-` | Windows workstation / server |
| `m365-` | Microsoft 365 / Entra |
| `od-` | Open Dental |
| `net-` | Networking |
| `_` | Meta / templates (not a runbook) |
## 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>` | Client / site identifier |
| `<SERVER>` | Server hostname |
| `<SHARE>` | Share name |
| `<SHARE_USER>` | Local account used for share access |
| `<USER>` | End-user account |
| `<PASSWORD>` | From password manager — never written to a file |
## Contents
| File | Purpose |
|---|---|
| [`od-smb-cred.md`](od-smb-cred.md) | Open Dental SMB share — stored-credential fix |
| [`cg-disable.ps1`](cg-disable.ps1) | Disable Credential Guard, then reboot (prompts to confirm) |
## 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.
+60
View File
@@ -0,0 +1,60 @@
<#
.SYNOPSIS
One-line description of what this runbook script does.
.DESCRIPTION
Longer context: symptom it addresses, what it changes, whether it reboots.
.NOTES
Convention for scripts in this repo — designed to run via:
irm https://gitea.ivangodwin.com/ops/rb/raw/branch/main/<file>.ps1 | iex
Because `irm | iex` runs in the caller's session:
- No param() block — you can't pass args through the pipe. Prompt with
Read-Host instead.
- #Requires is NOT enforced under iex — check for admin manually below.
- Keep it self-contained: no external module installs, no dot-sourcing.
PUBLIC REPO: placeholders only. Never hard-code a client, host, user, or
secret. Prompt for them at run time.
#>
$ErrorActionPreference = 'Stop'
# --- Admin check (do not rely on #Requires under iex) ---
$isAdmin = ([Security.Principal.WindowsPrincipal] `
[Security.Principal.WindowsIdentity]::GetCurrent()
).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
if (-not $isAdmin) {
Write-Warning 'This script needs an elevated PowerShell session. Re-run as Administrator.'
return
}
Write-Host '== <SCRIPT TITLE> ==' -ForegroundColor Cyan
# --- Prompt for placeholders (no param block; iex-safe) ---
$server = Read-Host 'Server hostname <SERVER>'
$shareUser = Read-Host 'Share account <SHARE_USER>'
# For secrets, use -AsSecureString and never echo or persist them:
# $secure = Read-Host 'Password <PASSWORD>' -AsSecureString
# --- Confirm before destructive / disruptive actions ---
Write-Host ''
Write-Host "About to: <describe the change> on $server" -ForegroundColor Yellow
if ((Read-Host 'Proceed? (y/N)') -ne 'y') {
Write-Host 'Aborted. No changes made.'
return
}
# --- Do the work ---
try {
# ... the actual commands ...
Write-Host 'Done.' -ForegroundColor Green
}
catch {
Write-Warning "Failed: $($_.Exception.Message)"
return
}
# --- If a reboot is required, confirm separately ---
# if ((Read-Host 'Reboot now to apply? (y/N)') -eq 'y') { shutdown /r /t 0 }
+59
View File
@@ -0,0 +1,59 @@
<#
.SYNOPSIS
Disable Windows Credential Guard, then reboot (prompts to confirm).
.DESCRIPTION
Credential Guard blocks replay of saved Credential Manager entries — the
classic "works after a manual Explorer connect, breaks on restart" SMB
symptom. This clears the VBS/Credential Guard flags and reboots to apply.
Enabled by default on Windows 11 22H2+ on entitled SKUs (Enterprise /
Business), not plain Pro.
.NOTES
Run via: irm https://gitea.ivangodwin.com/ops/rb/raw/branch/main/cg-disable.ps1 | iex
Referenced by od-smb-cred.md, Step 3.
If Credential Guard is still running after reboot, it was enabled with a
UEFI lock (needs the bcdedit / physical-presence removal), or MDM policy is
re-enabling it — align with the environment baseline instead of fighting it
locally.
#>
$ErrorActionPreference = 'Stop'
$isAdmin = ([Security.Principal.WindowsPrincipal] `
[Security.Principal.WindowsIdentity]::GetCurrent()
).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
if (-not $isAdmin) {
Write-Warning 'This script needs an elevated PowerShell session. Re-run as Administrator.'
return
}
Write-Host '== Disable Credential Guard ==' -ForegroundColor Cyan
Write-Host 'This clears the LsaCfgFlags / DeviceGuard Credential Guard flags and reboots.' -ForegroundColor Yellow
if ((Read-Host 'Proceed? (y/N)') -ne 'y') {
Write-Host 'Aborted. No changes made.'
return
}
try {
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" `
/v LsaCfgFlags /t REG_DWORD /d 0 /f | Out-Null
reg add "HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\CredentialGuard" `
/v Enabled /t REG_DWORD /d 0 /f | Out-Null
Write-Host 'Flags cleared.' -ForegroundColor Green
}
catch {
Write-Warning "Failed to write registry: $($_.Exception.Message)"
return
}
Write-Host ''
Write-Host 'A reboot is required. After reboot, re-run msinfo32 and confirm' -ForegroundColor Yellow
Write-Host 'Credential Guard is no longer listed under Virtualization-based security.' -ForegroundColor Yellow
if ((Read-Host 'Reboot now? (y/N)') -eq 'y') {
shutdown /r /t 0
} else {
Write-Host 'Skipped reboot. Changes apply on next restart.'
}
+134
View File
@@ -0,0 +1,134 @@
# Runbook: Open Dental SMB Share Access — Stored Credential Fix
**Applies to:** Entra-joined Windows workstation accessing an Open Dental A-to-Z share on a standalone (non-domain, non-Entra) server via local SAM credentials.
**Symptom:** Open Dental cannot reach `\\<SERVER>\<SHARE>` after workstation restart; works after manually connecting via File Explorer.
**Root causes covered:** missing/stale stored Windows credential; Credential Guard blocking saved credential replay; elevated process not seeing user-session credentials.
**Placeholders:**
| Placeholder | Meaning |
|---|---|
| `<SERVER>` | Application server hostname |
| `<SHARE>` | Share name (e.g. OpenDentImages) |
| `<SHARE_USER>` | Local account on `<SERVER>` used for share access |
| `<PASSWORD>` | From password manager — never stored in this file |
---
## 1. Confirm identity of the machine
```
hostname
```
Verify you're on the machine you think you're on before changing anything.
## 2. Check Credential Guard status
```
msinfo32
```
System Summary → **Virtualization-based security Services Running**
- Credential Guard **not listed** → skip to Step 4
- Credential Guard **listed** → do Step 3
> Context: Credential Guard blocks the replay of saved "Windows credentials" from Credential Manager. Symptom is exactly "works after manual Explorer connect, breaks on restart." Enabled by default on Windows 11 22H2+ on entitled SKUs (Enterprise/Business), not plain Pro.
## 3. Disable Credential Guard (ONLY if running)
Fastest, from an **elevated** PowerShell:
```
irm https://gitea.ivangodwin.com/ops/rb/raw/branch/main/cg-disable.ps1 | iex
```
It confirms, clears the flags, and prompts to reboot.
Offline / manual equivalent (elevated PowerShell or Command Prompt):
```
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" /v LsaCfgFlags /t REG_DWORD /d 0 /f
reg add "HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\CredentialGuard" /v Enabled /t REG_DWORD /d 0 /f
shutdown /r /t 0
```
After reboot: re-run msinfo32 and confirm Credential Guard is no longer listed, then continue.
> If it's still running after reboot, it was enabled with UEFI lock — requires the bcdedit/physical-presence removal procedure. Also check whether MDM policy is re-enabling it; align with the environment's baseline rather than fighting it locally.
## 4. Review existing stored credentials
Regular (NON-elevated) Command Prompt, logged in as the user who runs Open Dental:
```
cmdkey /list
```
- Record any entries for `<SERVER>` (by hostname or IP) before deleting.
- Delete existing entries for the server:
```
cmdkey /delete:<SERVER>
```
Repeat for IP-based entries if present. Duplicate entries for the same server (hostname + IP) cause intermittent 1219-style conflicts — clear all of them.
## 5. Add the credential
Same non-elevated prompt (elevated prompts write to the wrong credential vault):
```
cmdkey /add:<SERVER> /user:<SERVER>\<SHARE_USER> /pass:"<PASSWORD>"
```
Quote the password if it contains special characters.
## 6. Verify it stored
```
cmdkey /list
```
Expect an entry with `Target: <SERVER>`.
## 7. Cold test
```
shutdown /r /t 0
```
Log in as the Open Dental user. Launch Open Dental **directly — do not open Explorer or touch the share first.**
Open the Imaging module → confirm images load.
**PASS → done.** Record results.
## 8. If Step 7 fails — isolate
```
dir \\<SERVER>\<SHARE>
```
| Result | Meaning | Action |
|---|---|---|
| `dir` fails | Stored credential not being used | `cmdkey /list` — confirm entry survived reboot; re-check msinfo32 for Credential Guard |
| `dir` works, Open Dental doesn't | Open Dental launching elevated | Shortcut → Properties → Advanced → uncheck "Run as administrator"; also check Compatibility tab. Retest. If elevation is required, set `EnableLinkedConnections` (HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System, DWORD=1) and reboot. |
## Fleet comparison (optional, ~2 min)
On a known-working workstation:
```
cmdkey /list
```
Record what it stores for `<SERVER>` — documents the fleet's credential state and reveals inconsistencies (per-machine manual setups, stale accounts).
---
## Notes
- The Open Dental A-to-Z path stored in Setup → Data Paths is **global** (database-stored). The "Path override for this computer" field is per-workstation. Never change the global path to a mapped drive letter to fix one machine.
- Prefer a dedicated low-privilege local account on `<SERVER>` scoped to the share only — not an administrative account.
- Rotating the share account's password silently breaks stored credentials on every workstation using it. Inventory which machines hold it (Step 4 on each) before rotating.