# PowerShell Automation Center A unified framework for managing Windows automation tasks with a modular, extensible feature system. All configuration and execution flows through a single entry point: `configure.ps1`. ## Quick Start ```powershell # Navigate to the root folder cd C:\Tools\ArnePowershellAutomation # Run the main menu .\configure.ps1 ``` ## Architecture ### Entry Point: `configure.ps1` The only file you interact with directly. Provides a menu-driven interface for: - **Check**: View registration status of the background runner task - **Register**: Create a scheduled task that runs automation features at logon and every 2 minutes - **Remove**: Unregister the scheduled task - **Configure**: Adjust per-feature settings (toggles, custom values) ### Core Structure ``` ArnePowershellAutomation/ ├── configure.ps1 # Single user entry point └── internal/ ├── runner.ps1 # Scheduled task entry point (runs every 2 min + at logon) ├── lib/ # Shared utilities │ ├── Logging.ps1 # Centralized logging with rotation │ ├── Config.ps1 # Configuration and state I/O │ ├── Elevation.ps1 # UAC elevation helper │ ├── NetworkUtils.ps1 # DNS suffix detection │ └── ToastHelper.ps1 # Windows 11 toast notifications ├── features/ # Automation feature modules │ ├── DynamicLock.ps1 # Toggle Dynamic Lock on network presence │ ├── DefaultBrowser.ps1 # Monitor default browser setting │ └── SandwichReminder.ps1 # Time-based reminder notifications └── data/ ├── config.json # Feature toggles & settings (auto-seeded) ├── state/ │ └── state.json # Per-feature runtime state └── logs/ ├── automation-*.log # Daily rotating logs (7-day retention) ├── elevated-process.log # Elevation diagnostics ├── last-register-result.json # Last registration result └── last-remove-result.json # Last removal result ``` ### Execution Flow **At Logon / Every 2 Minutes:** 1. Scheduled task launches `internal/runner.ps1` 2. Runner loads all lib utilities and feature modules 3. For each enabled feature: - Reads configuration from `internal/data/config.json` - Reads previous state from `internal/data/state/state.json` - Calls `Invoke-Feature` function - Captures errors; one feature failure doesn't break others - Saves updated state back to file 4. Logs all activity to `internal/data/logs/automation-YYYY-MM-DD.log` **For Configuration Changes:** 1. User selects menu option 4 (Configure) 2. Navigate feature menu (numbers to toggle, C for sub-menu) 3. For each setting, enter value or accept default 4. Config is saved to `internal/data/config.json` immediately 5. Changes take effect on next scheduled task run ## Feature Contract Each feature in `internal/features/` must export: ### `$FeatureMeta` Variable ```powershell $FeatureMeta = @{ Name = "My Feature" Description = "What this feature does" Settings = @( @{ Key = "settingKey1"; Label = "Display Label"; Type = "string"; Default = "value"; Description = "Help text" } @{ Key = "settingKey2"; Label = "Port"; Type = "int"; Default = 8080; Description = "Port number" } @{ Key = "settingKey3"; Label = "Enabled"; Type = "bool"; Default = $true; Description = "Enable this?" } ) } ``` ### `Invoke-Feature` Function ```powershell function Invoke-Feature { param( [Parameter(Mandatory)][hashtable]$Config, # Feature settings from config.json [Parameter(Mandatory)][hashtable]$State # Feature state from state.json ) # Perform work here # Use Write-Log -Feature "MyFeature" for logging # Return updated $State hashtable return $State } ``` ### Built-in Utilities Available to Features - **Logging.ps1**: `Write-Log -Level (Info|Warn|Error) -Message -Feature` - **Config.ps1**: `Get-Config`, `Save-Config`, `Get-State`, `Save-State` - **NetworkUtils.ps1**: `Test-DnsSuffixConnected -Suffix` - **ToastHelper.ps1**: `Show-ToastNotification -Title -Body -Buttons` - **Elevation.ps1**: `Test-Administrator`, `Request-Elevation` ## Included Features ### DynamicLock Enables/disables Windows Dynamic Lock based on network connectivity. - **Settings**: Network domain suffix (e.g., "example.com"), revert timeout in minutes - **Logic**: When connected to configured network, enables Dynamic Lock (`EnableGoodbye`=1); when disconnected, reverts after timeout - **State Tracked**: Current connection status, last connected timestamp ### DefaultBrowser Monitors if your default browser matches expected ProgId (app identifier). - **Settings**: Target ProgId (e.g., "FirefoxURL", "OperaGXStable") - **Logic**: On each run, reads current ProgId; if mismatch, shows toast with link to settings; max once per day - **State Tracked**: Last notification date to avoid spam ### SandwichReminder Shows a toast reminder at a specific time on a specific network. - **Settings**: Reminder time (HH:MM), network domain suffix, reminder URL, notification window (±5 minutes) - **Logic**: If on configured network AND current time is within ±5 min of reminder time AND not shown today, display toast with link - **State Tracked**: Last notification date ## Usage Guide ### Main Menu ``` 1) Check Registration Status → Displays current task registration, last run time, next run time 2) Register Runner → Creates scheduled task (AtLogon trigger + 2-min repetition) → If not admin, prompts for UAC elevation → Updates internal/data/logs/last-register-result.json 3) Remove Runner → Unregisters the scheduled task → If not admin, prompts for UAC elevation → Updates internal/data/logs/last-remove-result.json 4) Configure Features → Submenu with feature toggles and per-feature settings → Enter number to toggle (on/off) → Enter C to configure settings → Q to return to main menu → Saves config.json immediately Q) Quit → Exits configure.ps1 ``` ### Configuring a Feature When you select a feature for configuration, you'll be prompted for each setting: - **Type: string** → Enter any text - **Type: int** → Enter a number - **Type: bool** → Enter Y/N (yes/no) - **Type: time** → Enter HH:MM format - Press Enter to accept the displayed default ## Testing Plan ### T1 — Basic Logging & Config Load - [ ] Run `.\configure.ps1` → Main menu displays - [ ] Verify `internal/data/config.json` was created (if first run) - [ ] Verify `internal/data/logs/automation-.log` has initialization entries ### T2 — Feature Discovery & Configuration Menu - [ ] Select menu option 4 (Configure) - [ ] Verify all three features appear (DynamicLock, DefaultBrowser, SandwichReminder) - [ ] Toggle DynamicLock (1) → Verify enabled flag flips in config.json - [ ] Select C1 (configure DynamicLock) → Modify network suffix, save, verify in config.json ### T3 — Registration & Task Scheduler - [ ] Select menu option 2 (Register) - [ ] If prompted for UAC, approve elevation - [ ] Verify success message and `last-register-result.json` shows success=true - [ ] Open Task Scheduler → Navigate to Library root → Verify "PSAutomation-Runner" task exists - [ ] Right-click task → Properties → Verify "Run as current user" (not elevated), AtLogOn trigger, 2-min repetition ### T4 — Feature Execution (DynamicLock) - [ ] Register the runner (option 2) - [ ] Enable DynamicLock in configuration menu (option 4) - [ ] Configure DynamicLock network to current network suffix (use `Get-DnsClient` or `ipconfig /all` to find) - [ ] Wait for runner to execute (at next 2-min interval or logon event) - [ ] Check `internal/data/logs/automation-.log` for "DynamicLock" entries - [ ] Verify state.json has updated `isConnected` flag ### T5 — Feature Execution (DefaultBrowser) - [ ] Enable DefaultBrowser in configuration menu - [ ] Verify toast notification appears (if configured ProgId differs from current default) - [ ] Click "Yes" button on toast → Should open Settings → Default apps - [ ] Check logs for "DefaultBrowser" entries and state.json for `lastShownDate` ### T6 — Feature Execution (SandwichReminder) - [ ] Enable SandwichReminder in configuration menu - [ ] Configure reminderTime to a time 1–2 minutes in the future (e.g., if current time is 14:35, set to 14:37) - [ ] Configure to current network suffix - [ ] Wait for runner to execute - [ ] Verify toast appears with configured URL - [ ] Check logs for "SandwichReminder" entries and state.json for `lastShownDate` ### T7 — Error Isolation - [ ] Intentionally break one feature (e.g., edit `internal/features/DynamicLock.ps1`, add invalid syntax) - [ ] Wait for runner to execute - [ ] Verify broken feature logged an error in automation-.log - [ ] Verify other features still executed successfully (logged their own entries) ## Known Limitations - **Default Browser Registry Protection**: On Windows 11, Microsoft protects the default browser registry key with a hash. The DefaultBrowser feature reads the ProgId but cannot *set* the default browser programmatically. The toast provides a link to Settings for manual override. - **Windows 11 Only for Toasts**: Toast notifications require WinRT (Windows 10+), but full Windows 11 support is assumed. Win10 may work but is untested. - **Scheduled Task Security**: The runner task executes as the current user with Limited RunLevel (no elevation). Features cannot perform system-wide administrative tasks; they're limited to user-level operations. - **DNS Suffix Matching**: Network detection relies on `Get-DnsClient` adapter DNS suffix. Make sure your network adapter is configured with the correct suffix for accurate detection. ## Extending with New Features To add a new automation feature: 1. Create `internal/features/MyFeature.ps1`: ```powershell #Requires -Version 5.1 param() # Empty; loaded via dot-source $FeatureMeta = @{ Name = "My Feature" Description = "Does something cool" Settings = @( @{ Key = "enabled"; Label = "Enable"; Type = "bool"; Default = $true; Description = "Turn on/off" } ) } function Invoke-Feature { param([hashtable]$Config, [hashtable]$State) if (-not $Config.enabled) { return $State } try { Write-Log -Level Info -Message "Running My Feature" -Feature "MyFeature" # Do work here } catch { Write-Log -Level Error -Message "Error: $_" -Feature "MyFeature" } return $State } ``` 2. Drop it in `internal/features/` — it will auto-discover on next configure.ps1 run 3. Run `.\configure.ps1` → menu option 4 → Your feature appears with toggles and config sub-menu ## Logging All activity is logged to `internal/data/logs/automation-YYYY-MM-DD.log` (UTC date). - **Retention**: 7 days (older logs auto-deleted) - **Format**: `[YYYY-MM-DD HH:mm:ss] [LEVEL] [Feature] Message` - **Levels**: Info, Warn, Error Elevation diagnostics go to `internal/data/logs/elevated-process.log` for troubleshooting registration issues. ## Troubleshooting **Task not running?** - Check Task Scheduler (root library) for "PSAutomation-Runner" task - Verify "Run as current user" and Limited RunLevel - Check `automation-.log` for errors **Features not executing?** - Verify feature is enabled in menu option 4 - Check `internal/data/config.json` for `"enabled": true` on that feature - Look for errors in `automation-.log` with feature name **Elevation prompts failing?** - Check `elevated-process.log` for bootstrap diagnostics - Ensure PowerShell isn't blocked by execution policy: `Get-ExecutionPolicy` - If needed, set: `Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser` ## License None. Use freely.