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:
@@ -0,0 +1 @@
|
||||
.DS_Store
|
||||
@@ -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`).
|
||||
@@ -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.
|
||||
@@ -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 }
|
||||
@@ -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
@@ -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.
|
||||
Reference in New Issue
Block a user