Arne Moerman 34ea1eb4b2 Add new features: DefaultBrowser, DynamicLock, SandwichReminder
- 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.
2026-05-08 11:48:39 +02:00

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:

  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

$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.json was created (if first run)
  • Verify internal/data/logs/automation-<date>.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-<date>.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 12 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:

    #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-<date>.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-<date>.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.

S
Description
No description provided
Readme 104 KiB
Languages
PowerShell 99.1%
VBScript 0.8%