- Implemented DefaultBrowser feature to notify users when the default browser does not match the configured app. - Added DynamicLock feature to disable Dynamic Lock while connected to a specific network and re-enable it after disconnecting. - Created SandwichReminder feature to prompt users to order a sandwich during work hours based on network and time settings. Introduced helper libraries for configuration, elevation, logging, network utilities, and toast notifications. - Config.ps1: Added functions for reading and writing configuration and state files. - Elevation.ps1: Added functions to check for administrator privileges and request elevation. - Logging.ps1: Implemented a shared logging utility for consistent logging across features. - NetworkUtils.ps1: Added a function to check for DNS suffix connectivity. - ToastHelper.ps1: Created a helper for displaying Windows toast notifications. Implemented runner.ps1 as the main entry point for executing features based on configuration.
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
# 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:
- Scheduled task launches
internal/runner.ps1 - Runner loads all lib utilities and feature modules
- For each enabled feature:
- Reads configuration from
internal/data/config.json - Reads previous state from
internal/data/state/state.json - Calls
Invoke-Featurefunction - Captures errors; one feature failure doesn't break others
- Saves updated state back to file
- Reads configuration from
- Logs all activity to
internal/data/logs/automation-YYYY-MM-DD.log
For Configuration Changes:
- User selects menu option 4 (Configure)
- Navigate feature menu (numbers to toggle, C for sub-menu)
- For each setting, enter value or accept default
- Config is saved to
internal/data/config.jsonimmediately - Changes take effect on next scheduled task run
Feature Contract
Each feature in internal/features/ must export:
$FeatureMeta Variable
$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
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<number> 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.jsonwas created (if first run) - Verify
internal/data/logs/automation-<date>.loghas 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.jsonshows 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-DnsClientoripconfig /allto find) - Wait for runner to execute (at next 2-min interval or logon event)
- Check
internal/data/logs/automation-<date>.logfor "DynamicLock" entries - Verify state.json has updated
isConnectedflag
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-DnsClientadapter 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:
-
Create
internal/features/MyFeature.ps1:#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 } -
Drop it in
internal/features/— it will auto-discover on next configure.ps1 run -
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-<date>.logfor errors
Features not executing?
- Verify feature is enabled in menu option 4
- Check
internal/data/config.jsonfor"enabled": trueon that feature - Look for errors in
automation-<date>.logwith feature name
Elevation prompts failing?
- Check
elevated-process.logfor 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.