# samy.functions.ps1 (3-in-1) This file groups three core categories of functionality used by SAMY: 1. **Bootstrap**: prepares PowerShell package tooling (NuGet provider, PowerShellGet, PackageManagement, PSGallery). 2. **Registry Helpers**: reusable functions to write registry values for current user, default user (future profiles), and existing user profiles. 3. **UI Handlers**: endpoint/task handlers invoked by the web UI (e.g., install 1Password, apply tweaks). The goal of this file is to provide “daily driver” functions SAMY needs across onboarding/offboarding workflows. > Encoding rule: save as **UTF-8 without BOM**. --- ## Contents overview - Bootstrap - `Initialize-NuGetProvider` - Task + validation helpers - `Test-ComputerName` - `Get-TaskHandlerName` - Registry helpers - `Set-RegistryValueInHkuRoot` - `Set-RegistryValueForCurrentAndAllUsers` - `Restart-ExplorerIfInteractive` - UI Handlers (examples included in file) - `Invoke-Install1Password` - `Invoke-DisableAnimations` - `Invoke-EnableNumLock` - `Invoke-ClassicContextMenu` --- ## Bootstrap ### Initialize-NuGetProvider **Purpose** Ensures SAMY can install/update PowerShell modules and providers without interactive prompts (critical for unattended runs and `iwr | iex` style execution). **Key behaviors** - Forces TLS 1.2 for network operations. - Silences progress + confirmation prompts to keep runs unattended. - Ensures provider folder paths exist: - Current user provider assemblies path - All users provider assemblies path (may fail without admin; handled) - Ensures the **NuGet package provider** is installed at a minimum version. - Imports NuGet provider after install. - Imports/updates: - `PackageManagement` - `PowerShellGet` - Optionally marks `PSGallery` as trusted to prevent prompts. **Why it matters** Many PowerShell module installs trigger interactive prompts, especially on fresh machines. This function makes module installation reliable for SAMY's automation. **Common failure modes** - No internet / proxy blocks PSGallery or NuGet endpoints. - Running under restricted execution policy or locked-down environment. - Running without elevation when trying to write to system-wide locations (handled as “warning” for folder creation). **Logging** Uses `Write-LogHybrid` with `-LogToEvent` to record bootstrap state and failures. **Modification notes** - If you need stricter behavior: choose whether to `throw` in the final catch block (PackageManagement/PowerShellGet setup). - If you need TLS 1.3+: be careful, many older environments still require TLS 1.2 compatibility. --- ## Task + validation helpers ### Test-ComputerName **Purpose** Validates a proposed computer name before applying it (typically from UI input). **Rules enforced** - Must not be null/empty/whitespace. - Must be **15 characters or fewer** (NetBIOS computer name limit). - Must match: `^[A-Za-z0-9-]+$` (letters, numbers, hyphen only). **Returns** - `$true` if name is valid - `$false` if name fails any rule **Where it’s used** - Typically in handlers such as rename/computer identity workflows, to prevent invalid names from being applied. **Maintenance notes** - If you want stricter rules (common): - disallow leading or trailing hyphen - disallow all-numeric names - Keep any rule changes consistent with your environment (AD join rules, naming standards, etc.). --- ### Get-TaskHandlerName **Purpose** Resolves the PowerShell function name that should execute for a given task object. This supports two patterns: 1. **Explicit handler name** stored on the task object (preferred when set) 2. **Convention-based handler name** derived from task `Name` **Resolution order** 1. Checks the task object for one of these properties (first match wins): - `HandlerFn` - `Handler` - `Fn` 2. If none are present/usable, falls back to: - `"Invoke-$Task.Name"` **Normalization** When a handler value is found: - Converts to string - Trims whitespace - Removes a leading `/` (in case the value is stored like a URL path) **Returns** - A string function name (e.g. `Invoke-RenameComputer`) when resolved - `$null` if no usable handler or name exists **Where it’s used** - The HTTP router / request dispatcher uses this to map a requested task route to the correct handler function. **Why it exists** - Allows tasks to follow a simple convention (`Invoke-`) while still supporting overrides for: - legacy handler names - special routing cases - tasks that map to a shared/common handler --- ## Registry Helpers ### Set-RegistryValueInHkuRoot **Purpose** Writes a registry value relative to a given `HKEY_USERS` hive root. **Parameters** - `HkuRoot`: registry root path (e.g. `Registry::HKEY_USERS\S-1-5-21-...`) - `RelativeKeyPath`: relative key path under the root - `Name`: value name - `Type`: value type (String, DWord, etc.) - `Value`: value data **Behavior** - Ensures the key exists. - Uses `New-ItemProperty` with `-Force` so it can create or overwrite. **When to use** When you already have the target user hive loaded and you want a consistent writer. --- ### Set-RegistryValueForCurrentAndAllUsers **Purpose** Applies the same registry setting to: 1. **Current user** (HKCU) if meaningful/available 2. **Default user profile hive** (`C:\Users\Default\NTUSER.DAT`) so future users inherit the setting 3. **All existing user profiles** by enumerating ProfileList and mounting each `NTUSER.DAT` as needed **High-level flow** 1. Try write to HKCU (best-effort; may be skipped under SYSTEM). 2. Default User: - Loads `C:\Users\Default\NTUSER.DAT` into `HKEY_USERS\SVS_DefaultUser` (if not already loaded) - Writes the setting - Unloads the hive (only if this function loaded it) 3. Existing profiles: - Enumerates `HKLM:\...\ProfileList` - Identifies user SIDs (`S-1-5-21-...`) - If hive already loaded under `HKEY_USERS\`, writes directly - Otherwise mounts `NTUSER.DAT` to a temporary key, writes, then unloads **Why it matters** Many Windows customizations are stored per-user. To make onboarding consistent, SAMY must update: - the active user (if running interactively) - the default profile (future logins) - every existing profile on the machine (shared devices, re-used devices) **Important safety notes** - Loading/unloading hives is sensitive: - A loaded hive can't always be unloaded if something else is holding a handle. - The function tracks whether it loaded the hive (`$didLoad`) to avoid unloading someone else's mount. - Profile iteration excludes non-standard SIDs by regex filter. - Some profiles may not have `NTUSER.DAT` (system profiles, corrupt profiles, etc.). Those are skipped safely. **Logging** Uses `Write-LogHybrid` to record default hive load/unload outcomes and per-setting success. **Modification notes** - If you add more settings, prefer calling this helper rather than duplicating hive logic. - If you need to write to HKLM machine-wide instead, do that outside this helper. - If you ever see “unload failed”, it's usually because something still holds a handle to the loaded hive. --- ### Restart-ExplorerIfInteractive **Purpose** Restarts Explorer to apply UI-related registry changes (taskbar, context menu, shell tweaks). **Behavior** - Skips restart when running as `SYSTEM` to avoid breaking non-interactive phases. - Stops `explorer.exe` and restarts it. **Modification notes** - If this ever runs during OOBE/Autopilot contexts, you may want stronger checks than `$env:USERNAME -ne 'SYSTEM'`. --- ## UI Handlers These functions are designed to be invoked by the SAMY web UI through task routing. Each handler: - parses optional JSON POST body (e.g., selected options) - executes the requested action - logs results via `Write-LogHybrid` - returns a response to the UI via `Send-Text` ### Invoke-Install1Password **Purpose** Installs 1Password desktop app and optionally forces browser extension install for Chrome and/or Edge. **Inputs** - Defaults to installing the desktop app if no UI selection is provided. - If POST JSON contains `checkedValues`, uses that list. **Actions** - Desktop app via `winget` (`AgileBits.1Password`) - Chrome extension by setting policy: - `HKLM:\SOFTWARE\Policies\Google\Chrome\ExtensionInstallForcelist` - Edge extension by setting policy: - `HKLM:\SOFTWARE\Policies\Microsoft\Edge\ExtensionInstallForcelist` **Notes** - Forcelist policy requires admin to write HKLM policies. - Browser extension IDs must remain correct; update if vendor changes. --- ### Invoke-DisableAnimations **Purpose** Applies UI “reduced motion” style tweaks by writing registry values across all users. **Default selection** If no options are provided, defaults to enabling all: - `window`, `taskbar`, `menus` **Settings applied** - Window animation: `MinAnimate` - Taskbar animations: `TaskbarAnimations` - Menu delay: `MenuShowDelay` **Post-action** - Restarts Explorer if taskbar changes were applied. --- ### Invoke-EnableNumLock **Purpose** Turns NumLock on by default (affects logon screen + default profile behavior). **Where it writes** - `Registry::HKEY_USERS\.DEFAULT\Control Panel\Keyboard` - `InitialKeyboardIndicators = "2"` (string) **Notes** - Uses `.DEFAULT` so it impacts the logon screen and baseline behavior. - This does not guarantee every vendor keyboard/BIOS setting, but covers the Windows side. --- ### Invoke-ClassicContextMenu **Purpose** Enables Windows 11 classic right-click context menu behavior. **Where it writes** - `HKCU:\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32` - Default value set to blank **Post-action** - Restarts Explorer to apply immediately. **Notes** - This is per-user (HKCU). If you want it for all users, use the registry helpers to apply across profiles. --- ## Logging expectations All functions log via `Write-LogHybrid` using categories like: - `Bootstrap` - `Tweaks` - `SVSApps` - `UI` When `-LogToEvent` is enabled, events are also written to the configured Windows Event Log (e.g., `SVSMSP Events`) using the project's Event Log binding logic. --- ## Common maintenance tasks ### Add a new UI handler 1. Create `Invoke-` function. 2. Parse POST JSON if needed (`checkedValues` pattern). 3. Use `Write-LogHybrid` for logging. 4. Use `Send-Text` for responses. 5. Ensure the router maps a route/task name to this handler. ### Add a new per-user tweak - Prefer using `Set-RegistryValueForCurrentAndAllUsers` to apply it to: - current user (HKCU) - Default User hive - existing profiles ### Debug “it works the second time” issues That often means initialization happens after first use (especially Event Logs and provider bootstrap). Ensure bootstrap/init happens before first call site that depends on it. --- ## Quick troubleshooting commands ### Check Event Log source binding ```powershell [System.Diagnostics.EventLog]::LogNameFromSourceName('SVMSP_Module','.')