param( [Parameter(Mandatory)] [string]$MacAddress, # e.g. "24-EE-9A-54-1B-E5" or "00:CE:39:D5:8C:AD" [Parameter(Mandatory)] [string]$TargetHost, # Hostname or IP address reachable when up "PortalControl" #[Parameter(Mandatory)] [string]$LogonUserName, # The account that auto-logs on (SAM name, e.g. "RoyF") [int]$WolPort = 9, [int]$OnlineTimeoutSec = 300, # Max wait for host to respond to ping [int]$LogonTimeoutSec = 300 # Max wait for user logon after host is online ) function Build-MagicPacket { param( [Parameter(Mandatory=$true)] [string]$MacAddress ) # Remove separators (colons or dashes) and convert to byte array $CleanMac = $MacAddress -replace '[: -]', '' $MacByteArray = for ($i = 0; $i -lt 12; $i += 2) { [Byte]::Parse($CleanMac.Substring($i, 2), 'HexNumber') } # Create the Magic Packet (6 bytes of 255 + 16 repetitions of the MAC address) $Packet = [Byte[]](@(0xFF) * 6) $Packet += $MacByteArray * 16 return $Packet } function Send-WoL { param( [Parameter(Mandatory=$true)] [String]$localIpAddresses, [Parameter(Mandatory=$true)] [Byte[]]$Packet ) # Create a UDP client bound to the local IP and broadcast to Port 9 $localEndpoint = [System.Net.IPEndPoint]::new(([System.Net.IPAddress]$localIpAddresses), 0) $targetEndpoint = [System.Net.IPEndPoint]::new([System.Net.IPAddress]::Broadcast, $WolPort) $client = [System.Net.Sockets.UdpClient]::new($localEndpoint) try { $client.Send($packet, $packet.Length, $targetEndpoint) | Out-Null } finally { $client.Dispose() } } function Get-ActiveNetworkAdapters { param( [Parameter(Mandatory=$true)] [PSCustomObject]$NetAdapters ) # Identify all network adapters that are currently 'Up' $NetAdapters | Where-Object {$_.Status -eq "Up"} | Get-NetIPAddress | Where-Object {$_.AddressFamily -eq "IPv4" -and $_.IPAddress -notlike "127.0.0.1"} | Select-Object InterfaceAlias, IPAddress } if (Test-Connection -ComputerName $TargetHost -Quiet -Count 1) { Write-Host "$TargetHost is already online." -ForegroundColor Green Exit 0 } # Identify all physical adapters that are currently 'Up' $physical = Get-NetAdapter -Physical -ErrorAction SilentlyContinue if ($null -ne $physical) { $physical = @(Get-ActiveNetworkAdapters -NetAdapters $physical) } else { $physical = @() } # Identify all Tailscale adapters that are currently 'Up' $tailscale = Get-NetAdapter -Name "Tailscale" -ErrorAction SilentlyContinue if ($null -ne $tailscale) { $tailscale = @(Get-ActiveNetworkAdapters -NetAdapters $tailscale) } else { $tailscale = @() } $magicPacket = Build-MagicPacket -MacAddress $MacAddress $activeAdapters = $physical #+ $tailscale $activeAdapters | ForEach-Object { Write-Host "Sending WoL packet via adapter: $($_.InterfaceAlias) $($_.IPAddress)" -ForegroundColor Cyan Send-WoL -localIpAddresses $_.IPAddress -Packet $magicPacket #Write-Host "WoL packet sent." -ForegroundColor Green } ########################################################################### # Write-Host "Sending Wake-on-LAN magic packet to $MacAddress ..." -ForegroundColor Cyan # Send-MagicPacket -Mac $MacAddress -Port $WolPort # Write-Host "Magic packet sent." -ForegroundColor Green # 1) Wait for host to respond to ping (online) $start = Get-Date Write-Host "Waiting for $TargetHost to respond to ping..." -ForegroundColor Cyan while (-not (Test-Connection -ComputerName $TargetHost -Quiet -Count 1)) { if ((Get-Date) - $start -gt [TimeSpan]::FromSeconds($OnlineTimeoutSec)) { #throw "Timeout waiting for $TargetHost to come online." Write-Warning "Timeout waiting for $TargetHost to come online. Exiting script." Exit 1 } Start-Sleep -Seconds 3 } Write-Host "$TargetHost is online." -ForegroundColor Green # 2) Wait for auto-logon of the specified user (Security event 4624) function Wait-ForUserLogon { param( [string]$TargetHost, [string]$LogonUserName, [DateTime]$Start, [int]$LogonTimeoutSec ) Write-Host "Waiting for successful logon event (4624) for user '$LogonUserName'..." -ForegroundColor Cyan $logonStart = Get-Date $logonFound = $false # Credential that has rights to read Security log on the target (often local admin) $cred = Get-Credential -Message "Enter credentials for $TargetHost (to read Security log)" while (-not $logonFound) { if ((Get-Date) - $logonStart -gt [TimeSpan]::FromSeconds($LogonTimeoutSec)) { throw "Timeout waiting for user '$LogonUserName' to log on to $TargetHost." } try { # Filter Security log on the remote host for event ID 4624 # and user name matching $LogonUserName $events = Get-WinEvent -ComputerName $TargetHost -Credential $cred -FilterHashtable @{ LogName = 'Security' Id = 4624 } -MaxEvents 50 foreach ($anEvent in $events) { # For 4624, Properties[5] typically holds the user account name.[web:68][web:71] $user = $anEvent.Properties[5].Value if ($user -and $user -ieq $LogonUserName) { # Optional: ensure event is recent (from this boot/wake) if ($anEvent.TimeCreated -gt $start) { $logonFound = $true Write-Host "Detected logon for '$LogonUserName' at $($anEvent.TimeCreated) on $TargetHost." -ForegroundColor Green break } } } } catch { Write-Warning "Error querying Security log on ${TargetHost}: $_" } if (-not $logonFound) { Start-Sleep -Seconds 5 } } Write-Host "Auto-logon confirmed; system should be ready for higher-level automation." -ForegroundColor Cyan } if (-not $LogonUserName) { Write-Warning "No LogonUserName specified; skipping logon wait." Exit 0 } else { Wait-ForUserLogon -TargetHost $TargetHost -LogonUserName $LogonUserName -Start $start -LogonTimeoutSec $LogonTimeoutSec }