P10: Windows Automation Suite (Capstone)

P10: Windows Automation Suite (Capstone)

Project Overview

What youโ€™ll build: A comprehensive automation platform that combines AutoHotkey v2 and PowerShell, featuring a system tray application with hotkeys for quick actions, a PowerShell backend module for complex operations, and seamless integration where AHK triggers PowerShell scripts and displays results through GUI notifications.

Attribute Value
Difficulty Expert
Time Estimate 1 month
Programming Languages AutoHotkey v2 + PowerShell
Knowledge Area System Administration, Desktop Automation, Inter-Process Communication
Prerequisites Completed at least 2-3 projects from each category (AHK and PowerShell)

Learning Objectives

After completing this project, you will be able to:

  1. Design hybrid automation systems - Architect solutions that leverage both AHK and PowerShell strengths
  2. Implement inter-process communication - Pass data between AHK and PowerShell reliably
  3. Build system tray applications - Create professional Windows applications with tray icons and menus
  4. Create toast notifications - Display modern Windows 10/11 notifications from scripts
  5. Develop a command palette interface - Build fuzzy-search command execution like VS Codeโ€™s Ctrl+Shift+P
  6. Handle scheduled automation - Run background tasks on timers with user-friendly alerts
  7. Manage configuration across tools - Share settings between AHK and PowerShell components
  8. Deploy multi-component systems - Package and distribute applications with multiple interacting parts

Deep Dive: Theoretical Foundation

Inter-Process Communication (IPC) Strategies

When AutoHotkey and PowerShell need to work together, they must communicate across process boundaries. Understanding IPC options is essential for building robust integrations.

Strategy 1: Command-Line Invocation (Simplest)

AHK launches PowerShell as a child process and captures output:

AHK Script
    |
    +-- Run("powershell.exe -Command Get-Service")
    |
    v
PowerShell executes, writes to stdout
    |
    v
AHK captures output via StdoutRead
    |
    v
AHK parses and displays results

Pros: Simple, no setup required, works everywhere Cons: Process startup overhead (~200-500ms), limited to text output

Strategy 2: Shared Files (Reliable)

Both tools read/write to shared configuration or data files:

AHK writes command:
    C:\Temp\automation_command.json
    { "action": "get-services", "filter": "SQL*" }
        |
        v
PowerShell watches file, executes, writes result:
    C:\Temp\automation_result.json
    { "services": [...], "timestamp": "..." }
        |
        v
AHK reads result file, displays GUI

Pros: Works across restarts, debuggable (files are visible), supports complex data Cons: File I/O latency, cleanup required, race conditions possible

Strategy 3: Named Pipes (Fast)

Create a named pipe for bidirectional communication:

PowerShell creates pipe server:
    \\.\pipe\automation_pipe
        |
AHK connects as client  <--->  PowerShell reads/writes
        |
Real-time bidirectional messaging

Pros: Very fast, real-time, no disk I/O Cons: Complex to implement, requires running server, connection management

Strategy 4: COM Objects (Deep Integration)

PowerShell exposes objects that AHK can call directly:

PowerShell registers COM object
    |
    v
AHK creates: ComObject("Automation.PowerShellBridge")
    |
    v
AHK calls: obj.GetServices("SQL*")
    |
    v
Returns actual objects, not just text

Pros: Richest integration, type-safe, fast after initial setup Cons: Requires registration, complex implementation, Windows-specific

Recommended Approach for This Project:

Use a hybrid strategy:

  1. Quick commands: Direct PowerShell invocation with JSON output
  2. Configuration: Shared JSON config file
  3. Background operations: File-based with file-watching
  4. Real-time monitoring: Named pipes for event streaming

When to Use AHK vs PowerShell: Decision Framework

Making the right tool choice is critical for maintainable automation. Use this framework:

Use AutoHotkey When:

Requirement Why AHK?
Keyboard shortcuts/hotkeys Native hotkey engine, sub-millisecond response
Mouse automation Direct cursor control, click simulation
GUI popup windows Built-in GUI system, lightweight windows
Clipboard management Native clipboard monitoring
Window manipulation WinMove, WinActivate, window enumeration
System tray icons Native tray menu support
User input blocking Can intercept and modify keystrokes

Use PowerShell When:

Requirement Why PowerShell?
System administration WMI/CIM queries, service management
File operations at scale Robust file handling, permissions
Remote execution PowerShell Remoting built-in
Complex data processing Pipeline paradigm, object manipulation
Windows Event Logs Native event log cmdlets
Scheduled Tasks Task Scheduler integration
Registry operations Full registry access
Active Directory AD cmdlets for user/group management

Decision Tree:

Is it user-facing with immediate feedback?
    |
    +-- Yes: Is it input-related (keyboard/mouse)?
    |       |
    |       +-- Yes: Use AHK
    |       |
    |       +-- No: Is it a simple popup/notification?
    |               |
    |               +-- Yes: Use AHK
    |               |
    |               +-- No: AHK for display, PowerShell for logic
    |
    +-- No: Is it system administration?
            |
            +-- Yes: Use PowerShell
            |
            +-- No: Does it need to run in background?
                    |
                    +-- Yes: PowerShell (or hybrid)
                    |
                    +-- No: Either works, prefer simpler option

System Tray Applications

A system tray (notification area) application provides persistent presence without consuming screen space. Windows users expect professional tools to live in the tray.

Anatomy of a tray application:

System Tray
    |
    +-- [Icon] <- Your application's icon
            |
            +-- Left-click: Show main window or primary action
            |
            +-- Right-click: Context menu
                    |
                    +-- Menu item 1
                    +-- Menu item 2
                    +-- Separator
                    +-- Exit

Key considerations:

  1. Icon design: Use clear, recognizable icons (16x16 and 32x32 pixels)
  2. Tooltip: Show application name and current status on hover
  3. Menu organization: Group related items, use separators
  4. Exit handling: Clean up resources before exiting
  5. Single instance: Prevent multiple copies running

AutoHotkey tray implementation:

A_TrayMenu.Delete()  ; Clear default menu
A_TrayMenu.Add("Command Palette", ShowPalette)
A_TrayMenu.Add("Settings", ShowSettings)
A_TrayMenu.Add()  ; Separator
A_TrayMenu.Add("Exit", ExitApp)
A_TrayMenu.Default := "Command Palette"  ; Double-click action

TraySetIcon("shell32.dll", 44)  ; Set custom icon

Toast Notifications on Windows

Modern Windows (10/11) provides the toast notification system for non-intrusive alerts. These appear in the Action Center and can include buttons, images, and rich text.

Toast notification architecture:

Your Script
    |
    v
BurntToast PowerShell Module (or WinRT APIs)
    |
    v
Windows Notification Platform
    |
    v
Action Center + Desktop Toast

Toast components:

Component Description
Title Bold header text
Body Main message content
Attribution Source application text
App Logo Small icon (optional)
Hero Image Large banner image (optional)
Buttons Action buttons (max 5)
Progress Bar For ongoing operations

PowerShell toast example (using BurntToast):

New-BurntToastNotification -Text "Automation Alert", "Server health check failed on SQL01" `
    -AppLogo "C:\Icons\warning.png" `
    -Button (New-BTButton -Content "View Details" -Arguments "view:healthcheck")

Fallback for systems without BurntToast:

# Use Windows Forms balloon notification
Add-Type -AssemblyName System.Windows.Forms
$balloon = New-Object System.Windows.Forms.NotifyIcon
$balloon.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon((Get-Process -Id $PID).Path)
$balloon.BalloonTipTitle = "Automation Alert"
$balloon.BalloonTipText = "Server health check completed"
$balloon.Visible = $true
$balloon.ShowBalloonTip(5000)

Script Integration Patterns

Integrating AHK and PowerShell requires consistent patterns for reliability and maintainability.

Pattern 1: Fire-and-Forget

AHK triggers PowerShell and doesnโ€™t wait for results:

; AHK: Trigger backup without waiting
Run('powershell.exe -WindowStyle Hidden -File "C:\Scripts\Backup.ps1"')
ToolTip("Backup started in background")

Use when: Long-running operations, no immediate feedback needed

Pattern 2: Synchronous Call with Output

AHK runs PowerShell and captures the result:

; AHK: Get service status and show in GUI
result := RunWaitOutput('powershell.exe -Command "Get-Service spooler | ConvertTo-Json"')
data := JSON.Parse(result)
MsgBox("Spooler status: " . data.Status)

Use when: Quick queries, results needed for display

Pattern 3: Async with Callback File

AHK triggers PowerShell, then monitors for result file:

; AHK: Start long operation
Run('powershell.exe -File "C:\Scripts\LongTask.ps1"')

; Monitor for completion
SetTimer(CheckTaskComplete, 1000)

CheckTaskComplete() {
    if FileExist("C:\Temp\task_complete.json") {
        result := FileRead("C:\Temp\task_complete.json")
        ; Process result, show notification
        SetTimer(CheckTaskComplete, 0)
    }
}

Use when: Long operations with result feedback

Pattern 4: Event Streaming

PowerShell runs continuously, AHK reads events:

PowerShell: Watches event log, writes events to file
    |
    v
C:\Temp\events_stream.log (continuously appended)
    |
    v
AHK: Tail-follows file, shows notifications for new entries

Use when: Continuous monitoring, real-time alerts

Configuration Management

Shared configuration ensures both AHK and PowerShell components work with the same settings.

Recommended: JSON configuration

{
    "version": "1.0.0",
    "hotkeys": {
        "commandPalette": "#Space",
        "quickDeploy": "#^d"
    },
    "paths": {
        "scripts": "C:\\AutomationSuite\\Scripts",
        "logs": "C:\\AutomationSuite\\Logs",
        "temp": "C:\\AutomationSuite\\Temp"
    },
    "servers": [
        {"name": "SQL01", "address": "192.168.1.10"},
        {"name": "SQL02", "address": "192.168.1.11"}
    ],
    "healthCheck": {
        "intervalMinutes": 30,
        "services": ["MSSQLSERVER", "SQLServerAgent"],
        "notifyOnFailure": true
    },
    "notifications": {
        "enabled": true,
        "useBurntToast": true,
        "soundEnabled": false
    }
}

Loading in AutoHotkey:

LoadConfig() {
    global Config
    configPath := A_ScriptDir . "\config.json"
    if FileExist(configPath) {
        content := FileRead(configPath)
        Config := JSON.Parse(content)
    }
}

Loading in PowerShell:

$ConfigPath = Join-Path $PSScriptRoot "config.json"
$Config = Get-Content $ConfigPath | ConvertFrom-Json

Complete Project Specification

Component Overview

Component Technology Purpose
System Tray Application AutoHotkey v2 Main entry point, hotkey registration, GUI
Command Palette AutoHotkey v2 Fuzzy-search command execution interface
PowerShell Backend PowerShell Module System operations, server management
Notification Engine PowerShell Toast notifications via BurntToast
Event Monitor PowerShell Background log/event watching
Configuration Shared JSON Settings for all components

Functional Requirements

ID Requirement Priority Component
F1 System tray icon with right-click menu Must Have AHK
F2 Hotkey-triggered command palette Must Have AHK
F3 Fuzzy search for commands Must Have AHK
F4 Execute PowerShell cmdlets from palette Must Have AHK + PS
F5 Display results in GUI popup Must Have AHK
F6 Scheduled health checks Must Have PS
F7 Desktop notifications for alerts Must Have PS + AHK
F8 Quick file deployment to servers Should Have PS
F9 Event log monitoring Should Have PS
F10 Custom command definitions Should Have Config
F11 Notification history Nice to Have AHK
F12 Remote server selection GUI Nice to Have AHK

Non-Functional Requirements

ID Requirement
NF1 Command palette appears within 100ms of hotkey
NF2 PowerShell operations complete within 30 seconds
NF3 Memory usage under 100MB total
NF4 Single-instance enforcement
NF5 Graceful degradation if PowerShell unavailable
NF6 Works without administrator privileges (except remote operations)

Real World Outcome

When complete, youโ€™ll have a professional automation suite:

System Tray Menu Structure

[Tray Icon: Lightning bolt]
    |
    Right-click menu:
    +-- Open Command Palette          [Win+Space]
    +-- โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    +-- Quick Actions >
    |       +-- Deploy Config Files
    |       +-- Restart SQL Services
    |       +-- Collect Server Logs
    +-- Server Status >
    |       +-- SQL01: [Running]
    |       +-- SQL02: [Running]
    |       +-- WEB01: [Warning]
    +-- โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    +-- Settings...
    +-- View Logs...
    +-- โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    +-- Exit

Command Palette Interaction

[Win+Space pressed]
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Automation Suite                                     [X]    โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                             โ”‚
โ”‚  > svc                                                      โ”‚
โ”‚  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€    โ”‚
โ”‚  > Get-ServiceStatus         Query service status           โ”‚
โ”‚  > Restart-RemoteService     Restart services on servers    โ”‚
โ”‚  > Stop-RemoteService        Stop services on servers       โ”‚
โ”‚                                                             โ”‚
โ”‚  [Enter] Run   [Tab] Details   [Esc] Close                  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

After selection:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Get-ServiceStatus                                           โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                             โ”‚
โ”‚  Service Name: SQL*                                         โ”‚
โ”‚  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€             โ”‚
โ”‚                                                             โ”‚
โ”‚  Server(s):                                                 โ”‚
โ”‚  [x] SQL01     [x] SQL02     [ ] WEB01                      โ”‚
โ”‚                                                             โ”‚
โ”‚  [Run]  [Cancel]                                            โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Results display:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Service Status Results                              [X]     โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                             โ”‚
โ”‚  Server        Service           Status      StartType      โ”‚
โ”‚  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€      โ”‚
โ”‚  SQL01         MSSQLSERVER       Running     Automatic      โ”‚
โ”‚  SQL01         SQLServerAgent    Running     Automatic      โ”‚
โ”‚  SQL02         MSSQLSERVER       Stopped     Automatic      โ”‚
โ”‚  SQL02         SQLServerAgent    Stopped     Automatic      โ”‚
โ”‚                                                             โ”‚
โ”‚  [Restart Stopped]  [Copy]  [Close]                         โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Notification Examples

Health Check Alert:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ [!] Automation Suite                     โ”‚
โ”‚                                          โ”‚
โ”‚ Health Check Failed                      โ”‚
โ”‚                                          โ”‚
โ”‚ SQL02: MSSQLSERVER is Stopped            โ”‚
โ”‚ 2 services require attention             โ”‚
โ”‚                                          โ”‚
โ”‚ [View Details]  [Restart Services]       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Event Log Alert:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ [!] Security Alert                       โ”‚
โ”‚                                          โ”‚
โ”‚ 15 failed login attempts detected        โ”‚
โ”‚ Source: 192.168.1.105                    โ”‚
โ”‚ Target: Administrator                    โ”‚
โ”‚                                          โ”‚
โ”‚ [View Event Log]  [Block IP]             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Configuration File Format

config.json:

{
    "$schema": "./config-schema.json",
    "version": "1.0.0",
    "application": {
        "name": "Automation Suite",
        "singleInstance": true,
        "startWithWindows": false,
        "minimizeToTray": true
    },
    "hotkeys": {
        "commandPalette": "#Space",
        "quickDeploy": "#^d",
        "serverStatus": "#^s",
        "emergencyRestart": "#^!r"
    },
    "paths": {
        "root": "C:\\AutomationSuite",
        "scripts": "${root}\\Scripts",
        "logs": "${root}\\Logs",
        "temp": "${root}\\Temp",
        "icons": "${root}\\Icons"
    },
    "servers": [
        {
            "name": "SQL01",
            "address": "sql01.corp.local",
            "group": "database",
            "enabled": true
        },
        {
            "name": "SQL02",
            "address": "sql02.corp.local",
            "group": "database",
            "enabled": true
        },
        {
            "name": "WEB01",
            "address": "web01.corp.local",
            "group": "web",
            "enabled": true
        }
    ],
    "commands": [
        {
            "id": "get-service-status",
            "name": "Get-ServiceStatus",
            "description": "Query service status on remote servers",
            "script": "Get-ServiceStatus.ps1",
            "category": "services",
            "parameters": [
                {"name": "ServiceName", "type": "string", "default": "*"},
                {"name": "ComputerName", "type": "server-select", "required": true}
            ]
        },
        {
            "id": "restart-service",
            "name": "Restart-RemoteService",
            "description": "Restart services on remote servers",
            "script": "Restart-RemoteService.ps1",
            "category": "services",
            "confirmRequired": true,
            "parameters": [
                {"name": "ServiceName", "type": "string", "required": true},
                {"name": "ComputerName", "type": "server-select", "required": true}
            ]
        },
        {
            "id": "deploy-config",
            "name": "Deploy-ConfigFile",
            "description": "Deploy configuration files to servers",
            "script": "Deploy-ConfigFile.ps1",
            "category": "deployment",
            "confirmRequired": true,
            "parameters": [
                {"name": "SourcePath", "type": "file-browse", "required": true},
                {"name": "DestinationPath", "type": "string", "required": true},
                {"name": "ComputerName", "type": "server-select", "required": true},
                {"name": "Backup", "type": "boolean", "default": true}
            ]
        }
    ],
    "healthCheck": {
        "enabled": true,
        "intervalMinutes": 30,
        "onStartup": true,
        "checks": [
            {
                "type": "service",
                "servers": ["SQL01", "SQL02"],
                "services": ["MSSQLSERVER", "SQLServerAgent"],
                "severity": "critical"
            },
            {
                "type": "service",
                "servers": ["WEB01"],
                "services": ["W3SVC"],
                "severity": "warning"
            }
        ],
        "notifications": {
            "onFailure": true,
            "onRecovery": true,
            "quietHoursStart": "22:00",
            "quietHoursEnd": "07:00"
        }
    },
    "eventMonitor": {
        "enabled": true,
        "rules": [
            {
                "name": "Failed Logins",
                "logName": "Security",
                "eventId": [4625],
                "threshold": 10,
                "windowMinutes": 5,
                "severity": "critical"
            },
            {
                "name": "Service Crash",
                "logName": "System",
                "eventId": [7034],
                "threshold": 1,
                "windowMinutes": 1,
                "severity": "warning"
            }
        ]
    },
    "notifications": {
        "enabled": true,
        "provider": "burnttoast",
        "fallbackToLegacy": true,
        "soundEnabled": true,
        "duration": "short"
    },
    "logging": {
        "level": "info",
        "maxSizeMB": 10,
        "retentionDays": 30
    }
}

Solution Architecture

Full System Diagram

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                              Windows Automation Suite                                 โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                                       โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚   โ”‚                         AutoHotkey v2 Frontend                               โ”‚   โ”‚
โ”‚   โ”‚                                                                               โ”‚   โ”‚
โ”‚   โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”‚   โ”‚
โ”‚   โ”‚   โ”‚  System Tray  โ”‚   โ”‚   Command     โ”‚   โ”‚   Result/Notification     โ”‚     โ”‚   โ”‚
โ”‚   โ”‚   โ”‚  Application  โ”‚   โ”‚   Palette     โ”‚   โ”‚   Display Windows         โ”‚     โ”‚   โ”‚
โ”‚   โ”‚   โ”‚               โ”‚   โ”‚               โ”‚   โ”‚                           โ”‚     โ”‚   โ”‚
โ”‚   โ”‚   โ”‚ - Icon/Menu   โ”‚   โ”‚ - Fuzzy searchโ”‚   โ”‚ - Data tables            โ”‚     โ”‚   โ”‚
โ”‚   โ”‚   โ”‚ - Hotkeys     โ”‚   โ”‚ - Param forms โ”‚   โ”‚ - Error displays         โ”‚     โ”‚   โ”‚
โ”‚   โ”‚   โ”‚ - Quick menu  โ”‚   โ”‚ - Execution   โ”‚   โ”‚ - Confirmation dialogs   โ”‚     โ”‚   โ”‚
โ”‚   โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ”‚   โ”‚
โ”‚   โ”‚           โ”‚                   โ”‚                         โ”‚                     โ”‚   โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                   โ”‚                                                   โ”‚
โ”‚                                   โ”‚ JSON / CLI                                        โ”‚
โ”‚                                   v                                                   โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚   โ”‚                         Integration Layer                                     โ”‚   โ”‚
โ”‚   โ”‚                                                                               โ”‚   โ”‚
โ”‚   โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”‚   โ”‚
โ”‚   โ”‚   โ”‚   PS Invoker      โ”‚   โ”‚   Config Manager  โ”‚   โ”‚   Event Bridge    โ”‚     โ”‚   โ”‚
โ”‚   โ”‚   โ”‚                   โ”‚   โ”‚                   โ”‚   โ”‚                   โ”‚     โ”‚   โ”‚
โ”‚   โ”‚   โ”‚ - RunWaitOutput   โ”‚   โ”‚ - Load config     โ”‚   โ”‚ - File watcher   โ”‚     โ”‚   โ”‚
โ”‚   โ”‚   โ”‚ - Async execution โ”‚   โ”‚ - Validate        โ”‚   โ”‚ - Event queue    โ”‚     โ”‚   โ”‚
โ”‚   โ”‚   โ”‚ - JSON parsing    โ”‚   โ”‚ - Hot-reload      โ”‚   โ”‚ - Alert dispatch โ”‚     โ”‚   โ”‚
โ”‚   โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ”‚   โ”‚
โ”‚   โ”‚             โ”‚                       โ”‚                       โ”‚                 โ”‚   โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                 โ”‚                                               ^                     โ”‚
โ”‚                 โ”‚ PowerShell invocation                         โ”‚ Events/Results     โ”‚
โ”‚                 v                                               โ”‚                     โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚   โ”‚                         PowerShell Backend Module                             โ”‚   โ”‚
โ”‚   โ”‚                                                                               โ”‚   โ”‚
โ”‚   โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”‚   โ”‚
โ”‚   โ”‚   โ”‚  Service Mgmt     โ”‚   โ”‚  File Operations  โ”‚   โ”‚  Event Monitor    โ”‚     โ”‚   โ”‚
โ”‚   โ”‚   โ”‚                   โ”‚   โ”‚                   โ”‚   โ”‚                   โ”‚     โ”‚   โ”‚
โ”‚   โ”‚   โ”‚ - Get-Status      โ”‚   โ”‚ - Deploy files    โ”‚   โ”‚ - Watch logs      โ”‚     โ”‚   โ”‚
โ”‚   โ”‚   โ”‚ - Start/Stop      โ”‚   โ”‚ - Collect logs    โ”‚   โ”‚ - Correlate       โ”‚     โ”‚   โ”‚
โ”‚   โ”‚   โ”‚ - Health checks   โ”‚   โ”‚ - Backup/restore  โ”‚   โ”‚ - Generate alerts โ”‚     โ”‚   โ”‚
โ”‚   โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ”‚   โ”‚
โ”‚   โ”‚             โ”‚                       โ”‚                       โ”‚                 โ”‚   โ”‚
โ”‚   โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”       โ”‚   โ”‚
โ”‚   โ”‚   โ”‚                     PowerShell Remoting                            โ”‚       โ”‚   โ”‚
โ”‚   โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜       โ”‚   โ”‚
โ”‚   โ”‚                                     โ”‚                                           โ”‚   โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                         โ”‚                                               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                          โ”‚
                                          v
        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
        โ”‚                      Remote Servers                              โ”‚
        โ”‚                                                                  โ”‚
        โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”               โ”‚
        โ”‚   โ”‚  SQL01   โ”‚     โ”‚  SQL02   โ”‚     โ”‚  WEB01   โ”‚    ...        โ”‚
        โ”‚   โ”‚  (WinRM) โ”‚     โ”‚  (WinRM) โ”‚     โ”‚  (WinRM) โ”‚               โ”‚
        โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜               โ”‚
        โ”‚                                                                  โ”‚
        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Component Interaction Flows

Flow 1: Command Palette Execution

1. User presses Win+Space
        |
        v
2. AHK: ShowCommandPalette()
        |
        +-- Load commands from config.json
        +-- Create searchable list
        +-- Show GUI
        |
        v
3. User types "svc" and selects "Get-ServiceStatus"
        |
        v
4. AHK: ShowParameterForm("get-service-status")
        |
        +-- Display parameter inputs
        +-- Show server checkboxes
        |
        v
5. User fills form and clicks [Run]
        |
        v
6. AHK: ExecutePowerShellCommand()
        |
        +-- Build command: powershell.exe -File Get-ServiceStatus.ps1 ...
        +-- Run with output capture
        |
        v
7. PowerShell: Get-ServiceStatus -ComputerName SQL01,SQL02 -ServiceName "SQL*"
        |
        +-- Connect to servers via Remoting
        +-- Query services
        +-- Format results as JSON
        +-- Write to stdout
        |
        v
8. AHK: ParseResults()
        |
        +-- Read stdout
        +-- Parse JSON
        +-- Display in results GUI

Flow 2: Health Check with Notification

1. Timer fires (every 30 minutes)
        |
        v
2. PowerShell: Invoke-HealthCheck
        |
        +-- Read health check config
        +-- For each check:
        |       +-- Connect to servers
        |       +-- Query services
        |       +-- Compare to expected state
        |
        v
3. PowerShell: Generate results
        |
        +-- If failures found:
        |       +-- Write to alerts file: C:\Temp\health_alerts.json
        |       +-- Exit with code 1
        +-- If all OK:
                +-- Exit with code 0
        |
        v
4. AHK: Check health check completion
        |
        +-- If exit code > 0:
        |       +-- Read alerts file
        |       +-- Parse JSON
        |       +-- Call: ShowNotification()
        |
        v
5. AHK: ShowNotification("Health Check Failed", details)
        |
        +-- If BurntToast available:
        |       +-- Invoke: New-BurntToastNotification
        +-- Else:
                +-- Show balloon tip

Flow 3: Event Log Monitoring

1. On startup: AHK triggers background PowerShell process
        |
        v
2. PowerShell: Start-EventMonitor (runs continuously)
        |
        +-- Read monitoring rules from config
        +-- For each rule:
        |       +-- Build XPath filter
        |       +-- Subscribe to event log
        |
        v
3. Event occurs matching a rule
        |
        v
4. PowerShell: Event handler fires
        |
        +-- Evaluate against threshold
        +-- If threshold exceeded:
        |       +-- Append to: C:\Temp\event_alerts.json
        |
        v
5. AHK: File watcher detects change
        |
        v
6. AHK: ReadNewAlerts()
        |
        +-- Parse new alerts
        +-- For each alert:
                +-- ShowNotification()

File Organization

C:\AutomationSuite\
โ”‚
โ”œโ”€โ”€ AutomationSuite.ahk              # Main AHK script (entry point)
โ”œโ”€โ”€ config.json                       # Shared configuration
โ”œโ”€โ”€ config-schema.json                # JSON schema for validation
โ”‚
โ”œโ”€โ”€ AHK\                              # AutoHotkey components
โ”‚   โ”œโ”€โ”€ Lib\
โ”‚   โ”‚   โ”œโ”€โ”€ JSON.ahk                  # JSON parser library
โ”‚   โ”‚   โ”œโ”€โ”€ GUI.ahk                   # GUI helper functions
โ”‚   โ”‚   โ”œโ”€โ”€ Tray.ahk                  # Tray icon management
โ”‚   โ”‚   โ””โ”€โ”€ PSInvoker.ahk             # PowerShell invocation helpers
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ CommandPalette.ahk            # Command palette implementation
โ”‚   โ”œโ”€โ”€ ResultsViewer.ahk             # Results display windows
โ”‚   โ”œโ”€โ”€ NotificationBridge.ahk        # Notification display
โ”‚   โ””โ”€โ”€ EventWatcher.ahk              # File/event watching
โ”‚
โ”œโ”€โ”€ PowerShell\                       # PowerShell module
โ”‚   โ”œโ”€โ”€ AutomationBackend\
โ”‚   โ”‚   โ”œโ”€โ”€ AutomationBackend.psd1    # Module manifest
โ”‚   โ”‚   โ”œโ”€โ”€ AutomationBackend.psm1    # Module loader
โ”‚   โ”‚   โ”‚
โ”‚   โ”‚   โ”œโ”€โ”€ Public\                   # Exported functions
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Get-ServiceStatus.ps1
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Restart-RemoteService.ps1
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Stop-RemoteService.ps1
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Deploy-ConfigFile.ps1
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Get-RemoteLogs.ps1
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Invoke-HealthCheck.ps1
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ Start-EventMonitor.ps1
โ”‚   โ”‚   โ”‚
โ”‚   โ”‚   โ”œโ”€โ”€ Private\                  # Internal functions
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Get-ServerSession.ps1
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Write-Alert.ps1
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ ConvertTo-AutomationOutput.ps1
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ Test-BurntToast.ps1
โ”‚   โ”‚   โ”‚
โ”‚   โ”‚   โ””โ”€โ”€ Classes\
โ”‚   โ”‚       โ”œโ”€โ”€ HealthCheckResult.ps1
โ”‚   โ”‚       โ””โ”€โ”€ EventAlert.ps1
โ”‚   โ”‚
โ”‚   โ””โ”€โ”€ Scripts\                      # Standalone scripts (called by AHK)
โ”‚       โ”œโ”€โ”€ Run-Command.ps1           # Generic command runner
โ”‚       โ”œโ”€โ”€ Quick-HealthCheck.ps1     # Quick status check
โ”‚       โ””โ”€โ”€ Send-Notification.ps1     # Toast notification sender
โ”‚
โ”œโ”€โ”€ Icons\                            # Application icons
โ”‚   โ”œโ”€โ”€ app.ico
โ”‚   โ”œโ”€โ”€ warning.ico
โ”‚   โ”œโ”€โ”€ error.ico
โ”‚   โ””โ”€โ”€ success.ico
โ”‚
โ”œโ”€โ”€ Logs\                             # Application logs
โ”‚   โ”œโ”€โ”€ automation.log
โ”‚   โ””โ”€โ”€ errors.log
โ”‚
โ””โ”€โ”€ Temp\                             # Temporary files for IPC
    โ”œโ”€โ”€ health_alerts.json
    โ”œโ”€โ”€ event_alerts.json
    โ””โ”€โ”€ command_results.json

Phased Implementation Guide

Phase 1: System Tray Skeleton (2-3 days)

Goal: Create a working system tray application with basic menu structure.

Tasks:

  1. Create main AHK script structure
    • Single-instance enforcement
    • Tray icon setup
    • Basic menu items
  2. Implement hotkey registration
    • Load hotkeys from config
    • Register with error handling
    • Test with placeholder actions
  3. Set up configuration loading
    • Create initial config.json
    • Implement JSON loading in AHK
    • Hot-reload on file change

Deliverables:

  • Tray icon visible and clickable
  • Menu with placeholder items
  • Win+Space shows tooltip โ€œCommand Palette coming soonโ€

Code Structure:

; AutomationSuite.ahk
#Requires AutoHotkey v2.0
#SingleInstance Force
Persistent

; Load libraries
#Include "AHK\Lib\JSON.ahk"
#Include "AHK\Lib\Tray.ahk"

; Global state
global Config := {}
global AppRunning := true

; Initialize
LoadConfig()
SetupTray()
RegisterHotkeys()

; Event loop
OnExit(CleanupAndExit)

; Functions
LoadConfig() {
    global Config
    configPath := A_ScriptDir . "\config.json"
    if FileExist(configPath) {
        content := FileRead(configPath)
        Config := JSON.Parse(content)
    }
}

SetupTray() {
    A_TrayMenu.Delete()
    A_TrayMenu.Add("&Command Palette", ShowCommandPalette)
    A_TrayMenu.Add()
    A_TrayMenu.Add("&Settings...", ShowSettings)
    A_TrayMenu.Add("View &Logs", ViewLogs)
    A_TrayMenu.Add()
    A_TrayMenu.Add("E&xit", ExitApp)
    A_TrayMenu.Default := "&Command Palette"

    TraySetIcon(A_ScriptDir . "\Icons\app.ico")
    A_IconTip := "Automation Suite"
}

RegisterHotkeys() {
    if Config.Has("hotkeys") {
        Hotkey(Config["hotkeys"]["commandPalette"], ShowCommandPalette)
    }
}

Phase 2: PowerShell Module Backend (3-4 days)

Goal: Create the PowerShell module with core functionality.

Tasks:

  1. Set up module structure
    • Create manifest (psd1)
    • Create module loader (psm1)
    • Organize public/private functions
  2. Implement core functions
    • Get-ServiceStatus (from P08)
    • Invoke-HealthCheck (from P05)
    • Session management helpers
  3. Standardize output format
    • JSON output for AHK consumption
    • Error handling with structured responses
    • Exit codes for status indication

Deliverables:

  • PowerShell module loads successfully
  • Get-ServiceStatus works standalone
  • JSON output is parseable

Example Function:

# Get-ServiceStatus.ps1
function Get-ServiceStatus {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string[]]$ComputerName,

        [string]$ServiceName = "*",

        [PSCredential]$Credential,

        [switch]$AsJson
    )

    $results = @()
    $errors = @()

    foreach ($server in $ComputerName) {
        try {
            $invokeParams = @{
                ComputerName = $server
                ScriptBlock = { param($svc) Get-Service -Name $svc -EA SilentlyContinue }
                ArgumentList = $ServiceName
                ErrorAction = 'Stop'
            }

            if ($Credential) { $invokeParams['Credential'] = $Credential }

            $services = Invoke-Command @invokeParams

            foreach ($svc in $services) {
                $results += [PSCustomObject]@{
                    ComputerName = $server
                    ServiceName = $svc.Name
                    DisplayName = $svc.DisplayName
                    Status = $svc.Status.ToString()
                    StartType = $svc.StartType.ToString()
                }
            }
        }
        catch {
            $errors += [PSCustomObject]@{
                ComputerName = $server
                Error = $_.Exception.Message
            }
        }
    }

    $output = @{
        Success = $errors.Count -eq 0
        Results = $results
        Errors = $errors
        Timestamp = (Get-Date -Format "o")
    }

    if ($AsJson) {
        return $output | ConvertTo-Json -Depth 10
    }
    return $output
}

Phase 3: AHK-PowerShell Integration (3-4 days)

Goal: Enable AHK to invoke PowerShell commands and parse results.

Tasks:

  1. Create PowerShell invoker class
    • Synchronous execution with output capture
    • Async execution with file-based results
    • Timeout handling
  2. Implement JSON parsing
    • Use/create JSON library for AHK v2
    • Handle nested objects and arrays
    • Error handling for malformed JSON
  3. Build result display framework
    • Generic data table GUI
    • Error display with details
    • Copy to clipboard functionality

Deliverables:

  • AHK can run PowerShell and get JSON back
  • Results display in GUI table
  • Errors show meaningful messages

Integration Code:

; PSInvoker.ahk
class PSInvoker {
    static powershellPath := "powershell.exe"
    static scriptPath := ""
    static timeout := 30000  ; 30 seconds

    static Init(scriptDir) {
        this.scriptPath := scriptDir . "\PowerShell\Scripts"
    }

    ; Synchronous execution with output
    static RunCommand(command, args := "") {
        fullCmd := this.powershellPath . ' -NoProfile -ExecutionPolicy Bypass '
        fullCmd .= '-Command "& {' . command . '} ' . args . ' | ConvertTo-Json -Depth 10"'

        shell := ComObject("WScript.Shell")
        exec := shell.Exec(fullCmd)

        ; Wait for completion with timeout
        startTime := A_TickCount
        while !exec.Status {
            if (A_TickCount - startTime > this.timeout) {
                exec.Terminate()
                return { success: false, error: "Timeout after " . (this.timeout/1000) . " seconds" }
            }
            Sleep(100)
        }

        if (exec.ExitCode != 0) {
            errOutput := exec.StdErr.ReadAll()
            return { success: false, error: errOutput }
        }

        output := exec.StdOut.ReadAll()
        try {
            data := JSON.Parse(output)
            return { success: true, data: data }
        } catch {
            return { success: false, error: "Failed to parse JSON: " . output }
        }
    }

    ; Run a script file
    static RunScript(scriptName, params := "") {
        scriptFile := this.scriptPath . "\" . scriptName
        if !FileExist(scriptFile) {
            return { success: false, error: "Script not found: " . scriptName }
        }

        fullCmd := this.powershellPath . ' -NoProfile -ExecutionPolicy Bypass '
        fullCmd .= '-File "' . scriptFile . '" ' . params

        shell := ComObject("WScript.Shell")
        exec := shell.Exec(fullCmd)

        ; Wait and capture output
        startTime := A_TickCount
        while !exec.Status {
            if (A_TickCount - startTime > this.timeout) {
                exec.Terminate()
                return { success: false, error: "Timeout" }
            }
            Sleep(100)
        }

        output := exec.StdOut.ReadAll()
        try {
            data := JSON.Parse(output)
            return { success: true, data: data }
        } catch {
            return { success: true, data: output }  ; Return raw if not JSON
        }
    }
}

Phase 4: Command Palette (4-5 days)

Goal: Build a VS Code-style command palette with fuzzy search.

Tasks:

  1. Create command registry
    • Load commands from config
    • Support categories and keywords
    • Enable/disable commands dynamically
  2. Implement fuzzy search
    • Character-by-character matching
    • Score-based ranking
    • Highlight matching characters
  3. Build parameter forms
    • Dynamic form generation
    • Server selection checkboxes
    • File browser integration
    • Validation before execution
  4. Connect execution pipeline
    • Build PowerShell command from form
    • Execute and show progress
    • Display results

Deliverables:

  • Command palette appears on hotkey
  • Fuzzy search filters commands
  • Parameters form works
  • Commands execute and show results

Command Palette Code:

; CommandPalette.ahk
class CommandPalette {
    static gui := ""
    static searchEdit := ""
    static commandList := ""
    static commands := []
    static filteredCommands := []

    static Show() {
        if (this.gui) {
            this.gui.Show()
            this.searchEdit.Focus()
            return
        }

        this.LoadCommands()
        this.CreateGUI()
    }

    static LoadCommands() {
        global Config
        this.commands := []

        if Config.Has("commands") {
            for cmd in Config["commands"] {
                this.commands.Push({
                    id: cmd["id"],
                    name: cmd["name"],
                    description: cmd["description"],
                    script: cmd["script"],
                    category: cmd["category"],
                    parameters: cmd.Has("parameters") ? cmd["parameters"] : [],
                    confirmRequired: cmd.Has("confirmRequired") ? cmd["confirmRequired"] : false
                })
            }
        }

        this.filteredCommands := this.commands
    }

    static CreateGUI() {
        this.gui := Gui("+AlwaysOnTop -MinimizeBox", "Automation Suite")
        this.gui.SetFont("s10")
        this.gui.OnEvent("Close", (*) => this.gui.Hide())
        this.gui.OnEvent("Escape", (*) => this.gui.Hide())

        ; Search box
        this.gui.Add("Text", "w400", "Type to search commands:")
        this.searchEdit := this.gui.Add("Edit", "w400 vSearchBox")
        this.searchEdit.OnEvent("Change", (*) => this.FilterCommands())

        ; Command list
        this.gui.Add("Text", "w400", "")
        this.commandList := this.gui.Add("ListView", "w400 h300 vCommandList", ["Command", "Description"])
        this.commandList.OnEvent("DoubleClick", (*) => this.ExecuteSelected())

        this.PopulateList()

        ; Buttons
        this.gui.Add("Button", "w100", "Run").OnEvent("Click", (*) => this.ExecuteSelected())
        this.gui.Add("Button", "x+10 w100", "Cancel").OnEvent("Click", (*) => this.gui.Hide())

        this.gui.Show()
        this.searchEdit.Focus()
    }

    static PopulateList() {
        this.commandList.Delete()
        for cmd in this.filteredCommands {
            this.commandList.Add(, cmd.name, cmd.description)
        }
        if (this.filteredCommands.Length > 0) {
            this.commandList.Modify(1, "Select Focus")
        }
    }

    static FilterCommands() {
        query := this.searchEdit.Value
        if (query = "") {
            this.filteredCommands := this.commands
        } else {
            this.filteredCommands := []
            for cmd in this.commands {
                if (this.FuzzyMatch(cmd.name . " " . cmd.description, query)) {
                    this.filteredCommands.Push(cmd)
                }
            }
        }
        this.PopulateList()
    }

    static FuzzyMatch(text, query) {
        text := StrLower(text)
        query := StrLower(query)

        queryPos := 1
        for i, char in StrSplit(text) {
            if (queryPos > StrLen(query))
                return true
            if (char = SubStr(query, queryPos, 1))
                queryPos++
        }
        return queryPos > StrLen(query)
    }

    static ExecuteSelected() {
        row := this.commandList.GetNext()
        if (row = 0)
            return

        cmd := this.filteredCommands[row]
        this.gui.Hide()

        if (cmd.parameters.Length > 0) {
            ParameterForm.Show(cmd)
        } else {
            CommandExecutor.Execute(cmd, {})
        }
    }
}

Phase 5: Notifications and Polish (4-5 days)

Goal: Implement notifications, health checks, and polish the experience.

Tasks:

  1. Implement notification bridge
    • BurntToast integration
    • Fallback to balloon tips
    • Action button handling
  2. Set up scheduled health checks
    • Timer-based execution
    • Background PowerShell process
    • Alert generation
  3. Build event monitor
    • PowerShell event watcher script
    • File-based alert communication
    • AHK file watcher
  4. Polish and testing
    • Error handling throughout
    • Logging implementation
    • Performance optimization
    • User experience refinements

Deliverables:

  • Toast notifications work
  • Health checks run on schedule
  • Event alerts appear in real-time
  • Application feels professional

Notification Bridge:

; NotificationBridge.ahk
class NotificationBridge {
    static useBurntToast := false
    static initialized := false

    static Init() {
        if (this.initialized)
            return

        ; Check if BurntToast is available
        result := PSInvoker.RunCommand('Get-Module BurntToast -ListAvailable')
        this.useBurntToast := (result.success && result.data != "")
        this.initialized := true
    }

    static Show(title, message, severity := "info", buttons := []) {
        this.Init()

        if (this.useBurntToast) {
            this.ShowToast(title, message, severity, buttons)
        } else {
            this.ShowBalloon(title, message, severity)
        }
    }

    static ShowToast(title, message, severity, buttons) {
        icon := this.GetIcon(severity)
        buttonParams := ""

        if (buttons.Length > 0) {
            buttonParams := "-Button "
            for btn in buttons {
                buttonParams .= '(New-BTButton -Content "' . btn.text . '" -Arguments "' . btn.action . '"),'
            }
            buttonParams := RTrim(buttonParams, ",")
        }

        cmd := 'New-BurntToastNotification -Text "' . title . '","' . message . '"'
        if (icon != "")
            cmd .= ' -AppLogo "' . icon . '"'
        if (buttonParams != "")
            cmd .= ' ' . buttonParams

        PSInvoker.RunCommand(cmd)
    }

    static ShowBalloon(title, message, severity) {
        iconType := severity = "error" ? 3 : (severity = "warning" ? 2 : 1)
        TrayTip(message, title, iconType)
    }

    static GetIcon(severity) {
        iconDir := A_ScriptDir . "\Icons\"
        switch severity {
            case "error": return iconDir . "error.ico"
            case "warning": return iconDir . "warning.ico"
            case "success": return iconDir . "success.ico"
            default: return iconDir . "app.ico"
        }
    }
}

Health Check Scheduler:

; HealthCheckScheduler.ahk
class HealthCheckScheduler {
    static timer := ""
    static lastCheck := 0
    static checkInProgress := false

    static Start() {
        global Config

        if (!Config.Has("healthCheck") || !Config["healthCheck"]["enabled"])
            return

        intervalMs := Config["healthCheck"]["intervalMinutes"] * 60 * 1000

        ; Run on startup if configured
        if (Config["healthCheck"]["onStartup"])
            this.RunCheck()

        ; Set up recurring timer
        SetTimer(ObjBindMethod(this, "RunCheck"), intervalMs)
    }

    static Stop() {
        SetTimer(ObjBindMethod(this, "RunCheck"), 0)
    }

    static RunCheck() {
        if (this.checkInProgress)
            return

        this.checkInProgress := true
        this.lastCheck := A_Now

        ; Run health check in background
        scriptPath := A_ScriptDir . "\PowerShell\Scripts\Quick-HealthCheck.ps1"
        alertPath := A_ScriptDir . "\Temp\health_alerts.json"

        ; Delete old alerts
        if FileExist(alertPath)
            FileDelete(alertPath)

        ; Run async
        Run('powershell.exe -WindowStyle Hidden -File "' . scriptPath . '"',, "Hide", &pid)

        ; Monitor for completion
        SetTimer(ObjBindMethod(this, "CheckCompletion", pid, alertPath), 1000)
    }

    static CheckCompletion(pid, alertPath) {
        ; Check if process still running
        try {
            proc := ProcessExist(pid)
            if (proc)
                return  ; Still running
        }

        ; Process completed
        SetTimer(, 0)  ; Stop this timer
        this.checkInProgress := false

        ; Check for alerts
        if FileExist(alertPath) {
            content := FileRead(alertPath)
            alerts := JSON.Parse(content)

            if (alerts.Has("failures") && alerts["failures"].Length > 0) {
                this.ShowHealthAlert(alerts)
            }
        }
    }

    static ShowHealthAlert(alerts) {
        global Config

        ; Check quiet hours
        if (this.InQuietHours())
            return

        failCount := alerts["failures"].Length
        firstFail := alerts["failures"][1]

        title := "Health Check Failed"
        message := firstFail["server"] . ": " . firstFail["service"] . " is " . firstFail["status"]
        if (failCount > 1)
            message .= "`n" . (failCount - 1) . " more issue(s) detected"

        NotificationBridge.Show(title, message, "warning", [
            { text: "View Details", action: "healthcheck:details" },
            { text: "Restart Services", action: "healthcheck:restart" }
        ])
    }

    static InQuietHours() {
        global Config
        if (!Config["healthCheck"]["notifications"].Has("quietHoursStart"))
            return false

        ; Parse quiet hours
        start := Config["healthCheck"]["notifications"]["quietHoursStart"]
        end := Config["healthCheck"]["notifications"]["quietHoursEnd"]

        ; Compare with current time
        nowTime := FormatTime(, "HH:mm")
        return (nowTime >= start || nowTime < end)
    }
}

Testing Strategy

Unit Tests

Component Test Expected Result
JSON Parser Parse valid JSON Object returned
JSON Parser Parse invalid JSON Error thrown
Fuzzy Match โ€œgssโ€ matches โ€œGet-ServiceStatusโ€ True
Fuzzy Match โ€œxyzโ€ matches โ€œGet-ServiceStatusโ€ False
PS Invoker Run valid command Success with data
PS Invoker Run with timeout Error returned
Config Loader Load valid config Config object populated
Config Loader Load missing file Defaults applied

Integration Tests

Flow Test Expected Result
Tray Application Start script Icon appears in tray
Tray Application Right-click icon Menu displays
Command Palette Press hotkey Palette appears < 100ms
Command Palette Type and select Command executes
PowerShell Get-ServiceStatus JSON results returned
Health Check Trigger check Alert if failures
Notifications Show toast Toast appears
Notifications Fallback Balloon if no BurntToast

End-to-End Tests

Scenario Steps Expected Outcome
Fresh Start Install, launch Tray icon, menu works
Service Query Palette > โ€œsvcโ€ > run Results in table
Deploy File Palette > deploy > select File on server
Health Failure Stop service, wait Notification appears
Event Alert Generate 15 failed logins Security alert shows

Performance Tests

Metric Target Test Method
Palette open time < 100ms Time from hotkey to visible
Service query (3 servers) < 10s End-to-end timing
Memory usage < 100MB Task Manager observation
Health check < 30s Timer measurement

Common Pitfalls and Debugging Tips

Pitfall 1: PowerShell execution policy blocks scripts

Symptoms: Scripts fail with โ€œcannot be loaded because running scripts is disabledโ€

Solution:

# Always use -ExecutionPolicy Bypass when invoking from AHK
powershell.exe -ExecutionPolicy Bypass -File "script.ps1"

Pitfall 2: JSON parsing fails with special characters

Symptoms: JSON.Parse throws error on valid-looking JSON

Causes:

  • Newlines in strings not escaped
  • Non-ASCII characters
  • BOM (byte order mark) in file

Solution:

; Sanitize JSON before parsing
content := StrReplace(content, "`r`n", "\n")
content := StrReplace(content, "`n", "\n")
content := RegExReplace(content, "^\xEF\xBB\xBF", "")  ; Remove BOM

Pitfall 3: WinRM not configured on target servers

Symptoms: โ€œWinRM cannot process the requestโ€

Solution:

# Run on each target server as Administrator
Enable-PSRemoting -Force
winrm quickconfig

Pitfall 4: Hotkey conflicts with other applications

Symptoms: Hotkey doesnโ€™t work or triggers wrong action

Solution:

; Check if hotkey is available before registering
try {
    Hotkey(hotkeyStr, callback)
} catch Error as e {
    MsgBox("Hotkey " . hotkeyStr . " is unavailable: " . e.Message)
}

; Use less common combinations
; Good: #!Space, ^#s
; Bad: ^c, ^v, #v (often taken)

Pitfall 5: File watcher misses changes

Symptoms: Events written but AHK doesnโ€™t see them

Causes:

  • File written but not flushed
  • Checking too infrequently
  • File locked by writer

Solution:

; Use polling with debounce
class FileWatcher {
    static lastModified := 0
    static debounceMs := 500

    static Check(filePath) {
        if !FileExist(filePath)
            return false

        modTime := FileGetTime(filePath, "M")
        if (modTime = this.lastModified)
            return false

        ; Debounce: wait for file to settle
        Sleep(this.debounceMs)

        newModTime := FileGetTime(filePath, "M")
        if (newModTime != modTime)
            return false  ; Still changing

        this.lastModified := newModTime
        return true
    }
}

Pitfall 6: PowerShell output not captured

Symptoms: RunCommand returns empty or null

Causes:

  • Output going to stderr
  • Non-text output (objects)
  • Encoding mismatch

Solution:

; Capture both stdout and stderr
fullCmd := 'powershell.exe -Command "& { ' . command . ' } 2>&1 | Out-String"'

; Ensure JSON output
fullCmd := 'powershell.exe -Command "& { ' . command . ' } | ConvertTo-Json -Depth 10"'

Pitfall 7: Single instance not working

Symptoms: Multiple copies of application running

Solution:

#SingleInstance Force  ; Must be at top of script

; Additional check via mutex
if (DllCall("CreateMutex", "Ptr", 0, "Int", 1, "Str", "AutomationSuiteMutex") = 0) {
    MsgBox("Another instance is already running")
    ExitApp()
}

Debugging Tips

  1. Enable verbose logging: ```autohotkey global DebugMode := true

Debug(msg) { global DebugMode if (DebugMode) { logPath := A_ScriptDir . โ€œ\Logs\debug.logโ€ FileAppend(A_Now . โ€œ: โ€œ . msg . โ€œ`nโ€, logPath) } }


2. **Test PowerShell commands directly**:
```powershell
# Run command manually to see full output
& "C:\AutomationSuite\PowerShell\Scripts\Get-ServiceStatus.ps1" -ComputerName localhost
  1. Use ToolTip for quick feedback:
    ToolTip("Step 1 complete")
    Sleep(1000)
    ToolTip("Step 2: " . variable)
    
  2. Check process output:
    result := PSInvoker.RunCommand("Get-Service")
    MsgBox("Success: " . result.success . "`nData: " . JSON.Stringify(result.data))
    

Extensions and Challenges

Easy Extensions

  1. Command history - Remember recently used commands and show them first
  2. Quick access favorites - Pin frequently used commands to tray menu
  3. Custom notification sounds - Different sounds for different alert types
  4. Keyboard navigation - Full keyboard control in all dialogs

Medium Extensions

  1. Plugin system - Load additional commands from external files
  2. Remote server groups - Organize servers by role/location
  3. Credential manager - Secure storage for multiple credentials
  4. Report generation - HTML reports for health checks and queries
  5. Backup/restore config - Export and import settings

Advanced Extensions

  1. Web dashboard - Simple local web server showing status
  2. Slack/Teams integration - Send alerts to chat channels
  3. Scheduled tasks - Define recurring automation jobs
  4. Multi-machine sync - Share commands across workstations
  5. RBAC - Role-based access control for sensitive commands
  6. Audit logging - Track who ran what commands when

Capstone Challenges

  1. Mobile companion app - Push notifications to phone
  2. Voice control - Execute commands via speech recognition
  3. AI assistant - Natural language command interpretation
  4. Self-healing automation - Auto-remediate common issues

Books That Will Help

Topic Book Chapter/Section
Windows automation architecture Windows Internals, Part 1 by Yosifovich et al. Chapter 1: Concepts and Tools
Inter-process communication Windows System Programming by Johnson M. Hart Chapter 11: Interprocess Communication
Event-driven programming Game Programming Patterns by Robert Nystrom Observer Pattern, Event Queue
PowerShell remoting Learn PowerShell in a Month of Lunches by Travis Plunk et al. Chapters 13-14: Remoting
PowerShell module development PowerShell in Depth by Don Jones et al. Part 4: PowerShell Modules
Configuration management The Pragmatic Programmer by Hunt & Thomas Chapter 17: Plain Text Configuration
System administration patterns The Practice of System and Network Administration by Limoncelli et al. Chapter 4: Automation
Notification design Donโ€™t Make Me Think by Steve Krug Chapter 6: Alerts and Notifications
Error handling Release It! by Michael Nygard Chapter 4: Stability Patterns
Testing automation Test-Driven Development by Kent Beck Chapters 1-5: Test Fundamentals

Self-Assessment Checklist

Phase 1: System Tray (Foundation)

  • Application starts and shows tray icon
  • Right-click menu displays with all items
  • Single instance enforcement works
  • Configuration loads from JSON
  • Hotkeys register without errors
  • Clean exit removes tray icon

Phase 2: PowerShell Backend (Engine)

  • PowerShell module loads successfully
  • Get-ServiceStatus works on local machine
  • Get-ServiceStatus works on remote servers
  • JSON output is valid and complete
  • Errors are captured and formatted
  • Session management works efficiently

Phase 3: Integration (Bridge)

  • AHK can invoke PowerShell commands
  • JSON results parse correctly
  • Results display in GUI table
  • Errors show meaningful messages
  • Timeout handling works
  • Async execution doesnโ€™t block UI

Phase 4: Command Palette (Interface)

  • Palette appears on hotkey < 100ms
  • Fuzzy search filters correctly
  • Parameter forms generate dynamically
  • Server selection works
  • Commands execute successfully
  • Results display after execution

Phase 5: Notifications (Alerts)

  • Toast notifications appear
  • Fallback to balloon tips works
  • Health checks run on schedule
  • Alerts appear for failures
  • Quiet hours are respected
  • Event monitoring works

Overall Quality

  • Memory usage stays under 100MB
  • No crashes during 8-hour test
  • All components log appropriately
  • Configuration changes apply without restart
  • Documentation is complete
  • Code is organized and readable

Final Notes

This capstone project synthesizes everything youโ€™ve learned about Windows automation:

  • From AutoHotkey projects: Event-driven programming, GUI creation, hotkey handling, clipboard management
  • From PowerShell projects: System administration, remoting, module development, error handling

The key insight is that the best automation combines both tools:

  • AHK for what the user touches (keyboard, mouse, screen)
  • PowerShell for what the system does (services, files, network)

Building this suite will prepare you for:

  • Enterprise automation roles
  • DevOps and SRE positions
  • Tool development for IT teams
  • Personal productivity mastery

Remember: The goal is not just to build the tool, but to understand when and how to combine different automation approaches effectively.

Good luck, and enjoy the journey from beginner to automation expert!