Files
PowershellAutomation/README.md
T

294 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
Clone or extract the repo to any location, then:
```powershell
# Navigate to the repo folder (any path works)
cd <your-repo-path>
# Run the main menu
.\configure.ps1
```
All paths are relative to `configure.ps1`, so the repo is portable.
## 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<number> 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<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-<date>.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.
- **Manual Execution & Repetition Timer**: Executing the runner manually via menu option 5 ("Execute runner now") runs the task immediately but does **not** start the 2-minute repetition timer. The repetition timer only activates on logon/restart. For continuous testing, you may need to restart or wait for next logon.
## 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-<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.