Skip to content

Automatic Hosts File

Developing with WSL is handy, but can get tricky when you need to access it from the Windows Host, such as accessing a web server.

This task will watch the Event Viewer for when Hyper-V loads network drivers and run the updateHosts.ps1 script. The IPv4 address of the eth0 interface will then be added to the Windows hosts file at wsl.local (or whatever name you choose).

Script

Much of this was taken from a site I found 1. I added the stderr null redirect because the wsl command when run in scripts complains about TTY size and will cause the script to fail.

Since this writes to a location within $env:windir, elevated privileges are required to run this.

The suggested directory to place this is $env:USERPROFILE\Documents\PowerShell\Scripts with the file name updateHosts.ps1.

updateHosts.ps1
$hostname = "wsl.local"

################################################################################

$ifconfig = (wsl -- ip -4 addr show eth0 2> $null)
$ipPattern = "((\d+\.?){4})"
$ip = ([regex]"inet $ipPattern").Match($ifconfig).Groups[1].Value
if (-not $ip) {
    exit
}
Write-Host $ip
$hostsPath = "$env:windir/system32/drivers/etc/hosts"
$hosts = (Get-Content -Path $hostsPath -Raw -ErrorAction Ignore)
if ($null -eq $hosts) {
    $hosts = ""
}
$hosts = $hosts.Trim()
$find = "$ipPattern\s+$hostname"
$entry = "$ip $hostname"
if ($hosts -match $find) {
    $hosts = $hosts -replace $find, $entry
} else {
    $hosts = "$hosts`n$entry".Trim()
}
try {
    $temp = "$hostsPath.new"
    New-Item -Path $temp -ItemType File -Force | Out-Null
    Set-Content -Path $temp $hosts
    Move-Item -Path $temp -Destination $hostsPath -Force
} catch {
    Write-Error "cannot update wsl ip"
}

Task

The following script creates a scheduled task that watches for loading Hyper-V network drivers.

This script must be run with elevated privileges. The above script is assumed to be placed in $env:USERPROFILE\Documents\PowerShell\Scripts and be named updateHosts.ps1. If the script is a different name or in a different location, change the $scriptLocation variable.

This script can be copied and paste inside an elevated PowerShell terminal.

createUpdateTask.ps1
$scriptLocation="$env:USERPROFILE\Documents\PowerShell\Scripts\updateHosts.ps1"

################################################################################

$trigger = Get-CimClass MSFT_TaskEventTrigger root/Microsoft/Windows/TaskScheduler `
    | New-CimInstance -ClientOnly

$trigger.Enabled = $true

$trigger.Subscription = @'
<QueryList>
  <Query Id="0" Path="System">
    <Select Path="System">
      *[System[Provider[@Name="Microsoft-Windows-Hyper-V-VmSwitch"] and EventID=102]]
    </Select>
  </Query>
</QueryList>
'@

$trigger.Delay = "PT5S" # 5 Seconds to allow WSL some time to initialize

$actionParams =  @{
    Execute  = "powershell.exe"
    Argument = "-WindowStyle hidden -ExecutionPolicy ByPass -File $scriptLocation"
}
$action    = New-ScheduledTaskAction @actionParams

$principal = New-ScheduledTaskPrincipal `
    -UserId "$env:USERDOMAIN\$env:USERNAME" `
    -LogonType ServiceAccount `
    -RunLevel Highest

$settings  = New-ScheduledTaskSettingsSet

$taskParams = @{
    TaskName    = "WSL Set Hosts File IP"
    Description = "Set WSL IP address in hosts file"
    TaskPath    = "\Event Viewer Tasks\"
    Action      = $action
    Principal   = $principal
    Settings    = $settings
    Trigger     = $trigger
}

Register-ScheduledTask @taskParams

Trigger Delay Format

Event trigger delays are expressed as a string starting with the letter P. T delimits date and time portions of the string. For more information, refer to the Task Scheduler's EventTrigger.Delay property documentation2.

Wrap-Up

Now shutdown and restart WSL.

wsl --shutdown
C:> bash -c "ip -4 addr show eth0"
Sleeping for 1 second to let systemd settle
4: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    inet 172.18.79.2/20 brd 172.18.79.255 scope global eth0
       valid_lft forever preferred_lft forever

Running bash will cause WSL to start if it's not running.

C:> ping wsl.local

Pinging wsl.local [172.18.79.2] with 32 bytes of data:
Reply from 172.18.79.2: bytes=32 time<1ms TTL=64
Reply from 172.18.79.2: bytes=32 time<1ms TTL=64
Reply from 172.18.79.2: bytes=32 time<1ms TTL=64
Reply from 172.18.79.2: bytes=32 time<1ms TTL=64

Ping statistics for 172.18.79.2:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms

You'll see that the IP addresses match.


Last update: April 2, 2022
Created: February 16, 2022