├── .gitignore ├── Install-DeterministicNetwork.ps1 ├── LICENSE.txt ├── README.md ├── scripts ├── HCN.ps1 ├── OutConsoleAndLog.psm1 ├── Register-DeterministicNetwork.ps1 └── sample.json └── tasks └── login-task.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.log -------------------------------------------------------------------------------- /Install-DeterministicNetwork.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Installation Script for the WSL2 Network Fix for Developers 4 | 5 | .DESCRIPTION 6 | Linux developers who choose (or are forced) to use Windows will benefit greatly 7 | from the use the the Windows Subsystem for Linux, and the Hyper-V virtualization 8 | engine. Unfortunately, these tools often run into problems with corporate use of 9 | private network ranges, especially when the developer using the system roams 10 | between remote and on-site work, or needs a VPN connection. 11 | 12 | The problem is that WSL and Hyper-V select private network ranges for internal use 13 | based on the networks that it can "see" when the system starts up. If the private 14 | networks in use change after startup, there may be a network collision. Networking 15 | inside the Hyper-V and WSL VMs then fail, and sometimes general networking on the 16 | host Windows system deteriorates as well. Microsoft does not appear to be 17 | interested in fixing this common problem. 18 | 19 | This tool will install a Loopback network adapter and startup and shutdown scripts 20 | that are designed to "trick" the WSL (and experimentally, Hyper-V) network collision 21 | avoidance algorithm into using a network range of our choosing that we know will not 22 | collide with our corporate internal private networks. 23 | 24 | .EXAMPLE 25 | PS> .\Install-DeterministicNetwork.ps1 -NetworkType WSL ` 26 | -GatewayAddress 172.30.0.1 ` 27 | -NetworkAddress 172.30.0.0/23 28 | 29 | "Class B Network" Example: 30 | Creates a WSL network adapter at 172.30.0.1 and netmask 255.255.254.0. This provides 31 | a range of addresses for WSL instances to use from 172.30.0.2 - 172.30.1.255 32 | 33 | .EXAMPLE 34 | PS> .\Install-DeterministicNetwork.ps1 -NetworkType WSL ` 35 | -GatewayAddress 10.10.10.1 ` 36 | -NetworkAddress 10.10.10.0/25 37 | 38 | "Class A Network" Example: 39 | Creates a WSL network adapter at 10.10.10.1 and netmask 255.255.255.128. This provides 40 | a range of addresses for WSL instances to use from 10.10.10.2 - 10.10.10.127 41 | 42 | .EXAMPLE 43 | PS> .\Install-DeterministicNetwork.ps1 -NetworkType Hyper-V ` 44 | -GatewayAddress 192.168.10.1 ` 45 | -NetworkAddress 192.168.10.0/24 46 | 47 | "Class C Network" Example: 48 | Creates a Hyper-V network adapter at 192.168.10.1 and netmask 255.255.255.0. This provides 49 | a range of addresses for WSL instances to use from 192.168.10.2 - 192.168.10.254 50 | 51 | .LINK 52 | https://github.com/jgregmac/hyperv-fix-for-devs 53 | #> 54 | [CmdletBinding()] 55 | param ( 56 | # Name of the dummy adapter that will be created. 57 | # Only testing this for WSL at present... will need to re-install Hyper-V to validate. 58 | [Parameter()] 59 | [ValidateSet("WSL", "Hyper-V")] 60 | [string] 61 | $NetworkType = "WSL", 62 | 63 | <# 64 | The IP address to be used on the interface created for WSL/Hyper-V. This will serve 65 | as the gateway address for the new virtual network. Examples: 66 | - 192.168.100.1 (Default) - A "class C" private network that is unlikely to collide with most home networks. 67 | - 172.16.100.1 - A "class B" private network, which might collide with a corporate network. 68 | - 10.100.100.1 - A "class A" private network, less likely to collide with a corporate network, but it could! 69 | #> 70 | [Parameter()] 71 | [IPaddress] 72 | $GatewayAddress = "192.168.100.1", 73 | 74 | <# 75 | The network address, in CIDR notation, to be assigned to the new virtual network. 76 | For explanation, see: 77 | For the "easy version", use: 78 | The default is 192.168.100.0/24. 79 | #> 80 | [Parameter()] 81 | [string] 82 | $NetworkAddress = "192.168.100.0/24", 83 | 84 | <# Target directory for the startup/shutdown scripts. Default is "$NetworkType-Network-Fix" 85 | directory under your user profile. #> 86 | [Parameter()] 87 | [string] 88 | $ScriptDestination = (Join-Path -Path $env:USERPROFILE -ChildPath "$NetworkType-Network-Fix") 89 | ) 90 | 91 | # Establish current path and logging: 92 | $CurrentPath = Split-Path $script:MyInvocation.MyCommand.Path -Parent 93 | Import-Module (Join-Path -Path $CurrentPath -ChildPath "\scripts\OutConsoleAndLog.psm1") -ea Stop 94 | $global:GlobalLog = (Join-Path -Path $CurrentPath -ChildPath "Install-Deterministric-$NetworkType-Network.log") 95 | if (Test-Path $GlobalLog) { Remove-Item -Path $GlobalLog -Force -Confirm:$false } 96 | 97 | Out-ConsoleAndLog "Starting installation of the $NetworkType Network Fix." 98 | Out-ConsoleAndLog "These messages will be logged to: $GlobalLog" 99 | 100 | # The installer will create the tasks to run under the account of the user running this 101 | # installer script. You could force installation for a specific user by changing these 102 | # variables, but the target user /has to/ have a working WSL instance to launch at login. 103 | # This scenario has not been tested, so it will remain a "constant" in the script for now. 104 | # Both the user name (in DomainName\UserName) and SID are required: 105 | $UserName = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name 106 | $UserSID = ([System.Security.Principal.WindowsIdentity]::GetCurrent()).User.Value 107 | Out-ConsoleAndLog "Generated Tasks will be run as $UserName with SID: $UserSID" 108 | 109 | #region Copy scripts to current user profile 110 | Out-ConsoleAndLog "Scripts will be copied to directory: $ScriptDestination" 111 | if (-not (Test-Path $ScriptDestination )) { 112 | Out-ConsoleAndLog "Creating script directory..." 113 | New-Item -ItemType Directory -Path $ScriptDestination -ea Stop -Force | Out-Null 114 | } 115 | #Write-Host "CurrentPath is: $CurrentPath" 116 | $ScriptSource = Join-Path -Path $CurrentPath -ChildPath scripts 117 | #Write-Host "ScriptSource is: $ScriptSource" 118 | Out-ConsoleAndLog "Copying scripts into place..." 119 | Copy-Item -Path (Join-Path -Path $ScriptSource -ChildPath "*.ps*1").ToString() ` 120 | -Destination $ScriptDestination -Force -Confirm:$false -ea Stop 121 | #endregion 122 | 123 | #region Create Scheduled Tasks 124 | $TaskSource = Join-Path -Path $CurrentPath -ChildPath tasks 125 | $TaskStage = Join-Path -Path $env:TEMP -ChildPath "$NetworkType-tasks" 126 | Out-ConsoleAndLog "Staging task definitions to: $TaskStage" 127 | if (-not (Test-Path $TaskStage)) { 128 | Out-ConsoleAndLog "Creating staging directory: '$TaskStage'..." 129 | New-Item -ItemType Directory -Path $TaskStage -Force -ea Stop | Out-Null 130 | } 131 | # Read the content of our scheduled task templates from the source, 132 | # Update the templates with local user data, and write to the $env:temp directory. 133 | Out-ConsoleAndLog "Updating the staged task definitions:" 134 | Get-ChildItem -Path $TaskSource | ForEach-Object { 135 | #Write-Host ("Working on source file: " + $_.FullName); 136 | $Leaf = $_.Name; 137 | Get-Content $_.FullName | 138 | ForEach-Object { $_ -replace "USER_ID", $UserName } | 139 | ForEach-Object { $_ -replace "USER_SID", $UserSID } | 140 | ForEach-Object { $_ -replace "STARTUP_SCRIPT_PATH", $ScriptDestination } | 141 | ForEach-Object { $_ -replace "NETWORK_TYPE", $NetworkType } | 142 | ForEach-Object { $_ -replace "IP_ADDRESS", $GatewayAddress } | 143 | ForEach-Object { $_ -replace "NETWORK_ADDRESS", $NetworkAddress } | 144 | Set-Content -Path (Join-Path -Path $TaskStage -ChildPath $Leaf) -Force -Confirm:$false -ea Stop; 145 | } 146 | # Register the login actions task 147 | Out-ConsoleAndLog "Registering the startup/login task..." 148 | $SourceFile = Join-Path -Path $TaskStage -ChildPath login-task.xml -Resolve 149 | Register-ScheduledTask -Xml (Get-Content $SourceFile | Out-String) ` 150 | -TaskName "$NetworkType Fix Task - On Startup" -Force -ea Stop | Out-Null 151 | # Remove-Item -Recurse -Path $TaskStage -Force 152 | #endregion 153 | 154 | Out-ConsoleAndLog "All done. $NetworkType Network Fix Startup Script has been installed." -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) wikiped. 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WSL2 Network Fix for Linux Developers (Deprecated) 2 | 3 | **NOTE**: The scripts contained in this repository no longer are needed on more recent 4 | versions of WSL on Windows 10 and 11, running WSL version 1.2.5 or later. On these 5 | newer versions of WSL, the Linux VM IP address range can be controlled using the 6 | following Windows registry keys: 7 | 8 | > HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss\NatNetwork 9 | > HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss\NatGatewayIpAddress 10 | 11 | Just set your preferred network range in CIDR format (i.e. 192.168.10.0/24) and set a 12 | compatible gateway address (i.e. 192.168.10.1), then reboot your system. 13 | 14 | Hopefully MS will expose a control for this in the wsl.exe command line on in the system 15 | settings in the near future. 16 | 17 | This instructions below still work, but given the simplicity of the registry-based 18 | solution, I strongly recommend migrating to that solution instead. 19 | 20 | Normally, Hyper-V/WSL uses a collision-avoidance algorithm when assigning private 21 | network ranges to the virtual networks that it creates for use by these services. 22 | This is fine for many use cases, but remote and roaming users on corporate networks 23 | may find this behavior unacceptable as the network that Windows thought was 24 | non-conflicting at system startup may become conflicting when you later start a VPN 25 | connection to your business network. The result is that your WSL or Hyper-V instances 26 | may lose outbound connectivity and bring your development work to a halt. 27 | 28 | This script allows you to specify a deterministic network range and gatweway to use 29 | for WSL. The network will be re-created on each startup to ensure 30 | continuity. _NOTE:_ WSL is the primary use case for this tool. Hyper-V support is 31 | _experimental_ and not yet reliable. 32 | 33 | This repository contains the script `Install-DeterministicNetwork.ps1`, which will create 34 | a scheduled task to register your preferred deterministic network ranges each time you 35 | login to Windows, and the script `Register-DeterministicNetwork.ps1`, which can be run 36 | on-demand. 37 | 38 | - [WSL2 Network Fix for Linux Developers (Deprecated)](#wsl2-network-fix-for-linux-developers-deprecated) 39 | - [Credit Where It Is Due](#credit-where-it-is-due) 40 | - [Alternatives](#alternatives) 41 | - [Prerequisites](#prerequisites) 42 | - [Usage](#usage) 43 | - [Background information](#background-information) 44 | - [Hyper-V? WSL? What do I do with these?](#hyper-v--wsl--what-do-i-do-with-these) 45 | - [Great! So what is the problem?](#great-so-what-is-the-problem) 46 | - [So what can I do about that? Get a Mac?](#so-what-can-i-do-about-that--get-a-mac) 47 | 48 | ## Credit Where It Is Due 49 | 50 | This solution really is just a PowerShell wrapper around HNS Network handline code 51 | that was developed by others. 52 | 53 | It is built most directly off the work of Sami Korhonen: 54 | 55 | 56 | Sami's code builds off of the HNS PowerShell module by "nwoodmsft" and "keithmange": 57 | 58 | 59 | Sami cites Biswa96's (Biswaprio Nath) code sample here as the inspiration for his work: 60 | 61 | 62 | Good work all in sorting out Microsoft's undocumented HNS network API! 63 | 64 | ## Alternatives 65 | 66 | A rather nice-looking and solution by [@wikiped](https://github.com/wikiped) can be found here: 67 | 68 | (This approach "does more", which is great if you need the additional functionality. I 69 | just want a simple and reliable mechanism for keeping the WSL network in a range of 70 | my choosing.) 71 | 72 | ## Prerequisites 73 | 74 | - You must have "Administrator" privileges on your system to run this script 75 | - This script tested only on Windows 10 21H1 and 11 21H2 with PowerShell 5 and 7.1. 76 | 77 | ## Usage 78 | 79 | 1. Start by cloning this repository, or downloading its contents to your system. You need the _entire_ 80 | repository contents, not just the Install-DeveloperFix.ps1 script. 81 | 2. Decide on a network range and gateway address for your new network, or use the defaults in this script. 82 | 3. Open a PowerShell prompt in the directory with the script and run the following commands: 83 | 84 | ```powershell 85 | # This script is not signed, so you need to set ExecutionPolicy to "RemoteSigned" or 86 | # "Unrestricted" to run it, if you have not already done so. 87 | Set-ExecutionPolicy -ExecutionPolicy Unrestricted 88 | 89 | # (If you download the code bundle from GitHub instead of cloning the repo, you may 90 | # need to "unblock" the scripts): 91 | Get-ChildItem -Include *.ps1,*.psm1 -Recurse | Unblock-File -Confirm:$false 92 | 93 | # Then just run the script. The parameters are optional and will default to: 94 | # WSL, 192.168.100.1, and 192.168.100.0/24, respectively. 95 | .\Install-DeterministicNetwork.ps1 [-NetworkType [ WSL | Hyper-V ]] [-GatewayAddress "IP_ADDRESS" ] [-NetworkAddress "NetworkAddressCIDR"] 96 | 97 | # (Optionally, you can revert to your original Execution Policy after the installation.) 98 | Set-ExecutionPolicy -ExecutionPolicy Restricted 99 | # Note: ExecutionPolicies are not true security boundaries. Most "serious" PowerShell 100 | # users will find that leaving the execution policy set to "Restricted" is impractcal at best. 101 | ``` 102 | 103 | Help is available though the usual PowerShell syntax: 104 | 105 | ```powershell 106 | # Simple Help: 107 | .\Install-DeterministicNetwork.ps1 -? 108 | or 109 | Get-Help .\Install-DeterministicNetwork.ps1 110 | 111 | # Detailed Help: 112 | Get-Help .\Install-DeterministicNetwork.ps1 -detailed 113 | 114 | # Full Help: 115 | Get-Help .\Install-DeterministicNetwork.ps1 -full 116 | ``` 117 | 118 | ## Background information 119 | 120 | ### Hyper-V? WSL? What do I do with these? 121 | 122 | Linux developers who choose (or are forced) to use Windows will benefit greatly 123 | from the use the the Windows Subsystem for Linux, and the Hyper-V virtualization 124 | engine. These are powerful tools that enable developers (and other IT pros 125 | such as systems administrators and analysts) to run Linux operating systems, tools, and 126 | containers quickly and efficiently, while still having access to the broad base of 127 | Windows productivity tools. 128 | 129 | ### Great! So what is the problem? 130 | 131 | Unfortunately, these tools often run into problems with corporate use of 132 | private network ranges, especially when the developer using the system roams 133 | between remote and on-site work, or needs a VPN connection. 134 | 135 | The comes from Hyper-V selecting private network ranges for internal use based 136 | on the networks that it can "see" when the system starts up. If the private networks 137 | in use change after startup, there may be a network collision. Networking inside 138 | the Hyper-V and WSL VMs then fail, and sometimes general networking on the host 139 | Windows system deteriorates as well. Microsoft does not appear to be interested in 140 | fixing this common problem. 141 | 142 | ### So what can I do about that? Get a Mac? 143 | 144 | Sure, you could get a Mac, or install Ubuntu. You also could use an alternative Linux 145 | run environment such as "Oracle VirtualBox", or VMware Workstation. OR... you can just 146 | run the "Install-DeterministicNetwork.ps1" script in this repository, and your life will be good again.* 147 | 148 | This tool will pre-create the "HNSNetwork" that WSL or Hyper-V network that Windows would create 149 | automatically, but using deterministic network ranges provided by you so that you don't get 150 | rando address ranges that create problems with your corporate network. 151 | 152 | *Ongoing life goodness not guaranteed. 153 | -------------------------------------------------------------------------------- /scripts/HCN.ps1: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # From: 3 | 4 | function Get-HnsClientNativeMethods() { 5 | $signature = @' 6 | // Networks 7 | 8 | [DllImport("computenetwork.dll")] 9 | public static extern System.Int64 HcnEnumerateNetworks( 10 | [MarshalAs(UnmanagedType.LPWStr)] 11 | string Query, 12 | [MarshalAs(UnmanagedType.LPWStr)] 13 | out string Networks, 14 | [MarshalAs(UnmanagedType.LPWStr)] 15 | out string Result); 16 | 17 | [DllImport("computenetwork.dll")] 18 | public static extern System.Int64 HcnCreateNetwork( 19 | [MarshalAs(UnmanagedType.LPStruct)] 20 | Guid Id, 21 | [MarshalAs(UnmanagedType.LPWStr)] 22 | string Settings, 23 | [MarshalAs(UnmanagedType.SysUInt)] 24 | out IntPtr Network, 25 | [MarshalAs(UnmanagedType.LPWStr)] 26 | out string Result); 27 | 28 | [DllImport("computenetwork.dll")] 29 | public static extern System.Int64 HcnOpenNetwork( 30 | [MarshalAs(UnmanagedType.LPStruct)] 31 | Guid Id, 32 | [MarshalAs(UnmanagedType.SysUInt)] 33 | out IntPtr Network, 34 | [MarshalAs(UnmanagedType.LPWStr)] 35 | out string Result); 36 | 37 | [DllImport("computenetwork.dll")] 38 | public static extern System.Int64 HcnModifyNetwork( 39 | [MarshalAs(UnmanagedType.SysUInt)] 40 | IntPtr Network, 41 | [MarshalAs(UnmanagedType.LPWStr)] 42 | string Settings, 43 | [MarshalAs(UnmanagedType.LPWStr)] 44 | out string Result); 45 | 46 | [DllImport("computenetwork.dll")] 47 | public static extern System.Int64 HcnQueryNetworkProperties( 48 | [MarshalAs(UnmanagedType.SysUInt)] 49 | IntPtr Network, 50 | [MarshalAs(UnmanagedType.LPWStr)] 51 | string Query, 52 | [MarshalAs(UnmanagedType.LPWStr)] 53 | out string Properties, 54 | [MarshalAs(UnmanagedType.LPWStr)] 55 | out string Result); 56 | 57 | [DllImport("computenetwork.dll")] 58 | public static extern System.Int64 HcnDeleteNetwork( 59 | [MarshalAs(UnmanagedType.LPStruct)] 60 | Guid Id, 61 | [MarshalAs(UnmanagedType.LPWStr)] 62 | out string Result); 63 | 64 | [DllImport("computenetwork.dll")] 65 | public static extern System.Int64 HcnCloseNetwork( 66 | [MarshalAs(UnmanagedType.SysUInt)] 67 | IntPtr Network); 68 | '@ 69 | 70 | # Compile into runtime type 71 | Add-Type -MemberDefinition $signature -Namespace ComputeNetwork.HNS.PrivatePInvoke -Name NativeMethods -PassThru 72 | } 73 | 74 | Add-Type -TypeDefinition @" 75 | public enum ModifyRequestType { 76 | Add, 77 | Remove, 78 | Update, 79 | Refresh 80 | }; 81 | 82 | public enum EndpointResourceType { 83 | Port, 84 | Policy, 85 | }; 86 | public enum NetworkResourceType { 87 | DNS, 88 | Extension, 89 | Policy, 90 | Subnet, 91 | Subnets, 92 | IPSubnet 93 | }; 94 | public enum NamespaceResourceType { 95 | Container, 96 | Endpoint, 97 | }; 98 | "@ 99 | 100 | $ClientNativeMethods = Get-HnsClientNativeMethods 101 | 102 | $NetworkNativeMethods = @{ 103 | Open = $ClientNativeMethods::HcnOpenNetwork; 104 | Close = $ClientNativeMethods::HcnCloseNetwork; 105 | Enumerate = $ClientNativeMethods::HcnEnumerateNetworks; 106 | Delete = $ClientNativeMethods::HcnDeleteNetwork; 107 | Query = $ClientNativeMethods::HcnQueryNetworkProperties; 108 | Modify = $ClientNativeMethods::HcnModifyNetwork; 109 | } 110 | 111 | ######### 112 | # Network 113 | 114 | function New-HnsNetworkEx { 115 | param ( 116 | [parameter(Mandatory=$true)] [Guid] $Id, 117 | [parameter(Mandatory=$true, Position=0)] 118 | [string] $JsonString 119 | ) 120 | 121 | $settings = $JsonString 122 | $handle = 0 123 | $result = "" 124 | $hnsClientApi = Get-HnsClientNativeMethods 125 | $hr = $hnsClientApi::HcnCreateNetwork($id, $settings, [ref] $handle, [ref] $result); 126 | ReportErrorsEx -FunctionName HcnCreateNetwork -Hr $hr -Result $result -ThrowOnFail 127 | 128 | $query = '{"SchemaVersion": { "Major": 1, "Minor": 0 }}' 129 | $properties = ""; 130 | $result = "" 131 | $hr = $hnsClientApi::HcnQueryNetworkProperties($handle, $query, [ref] $properties, [ref] $result); 132 | ReportErrorsEx -FunctionName HcnQueryNetworkProperties -Hr $hr -Result $result 133 | $hr = $hnsClientApi::HcnCloseNetwork($handle); 134 | ReportErrorsEx -FunctionName HcnCloseNetwork -Hr $hr 135 | 136 | return ConvertResponseFromJsonEx -JsonInput $properties 137 | } 138 | 139 | function Get-HnsNetworkEx { 140 | param ( 141 | [parameter(Mandatory=$false)] [Guid] $Id = [Guid]::Empty, 142 | [parameter(Mandatory=$false)] [switch] $Detailed, 143 | [parameter(Mandatory=$false)] [int] $Version 144 | ) 145 | if($Detailed.IsPresent) { 146 | return Get-HnsGenericEx -Id $Id -NativeMethods $NetworkNativeMethods -Version $Version -Detailed 147 | } 148 | else { 149 | return Get-HnsGenericEx -Id $Id -NativeMethods $NetworkNativeMethods -Version $Version 150 | } 151 | } 152 | 153 | function Remove-HnsNetworkEx { 154 | [CmdletBinding()] 155 | param ( 156 | [parameter(Mandatory=$true,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] 157 | [Object[]] $InputObjects 158 | ) 159 | begin {$objects = @()} 160 | process {$Objects += $InputObjects;} 161 | end { 162 | Remove-HnsGenericEx -InputObjects $Objects -NativeMethods $NetworkNativeMethods 163 | } 164 | } 165 | 166 | ######### 167 | # Generic 168 | 169 | function Get-HnsGenericEx { 170 | param ( 171 | [parameter(Mandatory=$false)] [Guid] $Id = [Guid]::Empty, 172 | [parameter(Mandatory=$false)] [Hashtable] $Filter = @{}, 173 | [parameter(Mandatory=$false)] [Hashtable] $NativeMethods, 174 | [parameter(Mandatory=$false)] [switch] $Detailed, 175 | [parameter(Mandatory=$false)] [int] $Version 176 | ) 177 | 178 | $ids = "" 179 | $FilterString = ConvertTo-Json $Filter -Depth 32 180 | $query = @{Filter = $FilterString } 181 | if($Version -eq 2) { 182 | $query += @{SchemaVersion = @{ Major = 2; Minor = 0 }} 183 | } 184 | else { 185 | $query += @{SchemaVersion = @{ Major = 1; Minor = 0 }} 186 | } 187 | if($Detailed.IsPresent) { 188 | $query += @{Flags = 1} 189 | } 190 | $query = ConvertTo-Json $query -Depth 32 191 | if ($Id -ne [Guid]::Empty) { 192 | $ids = $Id 193 | } 194 | else { 195 | $result = "" 196 | $hr = $NativeMethods["Enumerate"].Invoke($query, [ref] $ids, [ref] $result); 197 | ReportErrorsEx -FunctionName $NativeMethods["Enumerate"].Name -Hr $hr -Result $result -ThrowOnFail 198 | 199 | if($ids -eq $null) { 200 | return 201 | } 202 | 203 | $ids = ($ids | ConvertFrom-Json) 204 | } 205 | 206 | $output = @() 207 | $ids | ForEach-Object { 208 | $handle = 0 209 | $result = "" 210 | $hr = $NativeMethods["Open"].Invoke($_, [ref] $handle, [ref] $result); 211 | ReportErrorsEx -FunctionName $NativeMethods["Open"].Name -Hr $hr -Result $result 212 | $properties = ""; 213 | $result = "" 214 | $hr = $NativeMethods["Query"].Invoke($handle, $query, [ref] $properties, [ref] $result); 215 | ReportErrorsEx -FunctionName $NativeMethods["Query"].Name -Hr $hr -Result $result 216 | $output += ConvertResponseFromJsonEx -JsonInput $properties 217 | $hr = $NativeMethods["Close"].Invoke($handle); 218 | ReportErrorsEx -FunctionName $NativeMethods["Close"].Name -Hr $hr 219 | } 220 | 221 | return $output 222 | } 223 | 224 | function Remove-HnsGenericEx { 225 | param ( 226 | [parameter(Mandatory = $false, ValueFromPipeline = $True, ValueFromPipelinebyPropertyName = $True)] 227 | [Object[]] $InputObjects, 228 | [parameter(Mandatory=$false)] [Hashtable] $NativeMethods 229 | ) 230 | 231 | begin {$objects = @()} 232 | process { 233 | if($InputObjects) { 234 | $Objects += $InputObjects; 235 | } 236 | } 237 | end { 238 | $Objects | Foreach-Object { 239 | $result = "" 240 | $hr = $NativeMethods["Delete"].Invoke($_.Id, [ref] $result); 241 | ReportErrorsEx -FunctionName $NativeMethods["Delete"].Name -Hr $hr -Result $result 242 | } 243 | } 244 | } 245 | 246 | ######### 247 | # Helpers 248 | 249 | function ReportErrorsEx { 250 | param ( 251 | [parameter(Mandatory=$false)] 252 | [string] $FunctionName, 253 | [parameter(Mandatory=$true)] 254 | [Int64] $Hr, 255 | [parameter(Mandatory=$false)] 256 | [string] $Result, 257 | [switch] $ThrowOnFail 258 | ) 259 | 260 | $errorOutput = "" 261 | 262 | if($Hr -ne 0) { 263 | $errorOutput += "HRESULT: $($Hr). " 264 | } 265 | 266 | if(-NOT [string]::IsNullOrWhiteSpace($Result)) { 267 | $errorOutput += "Result: $($Result)" 268 | } 269 | 270 | if(-NOT [string]::IsNullOrWhiteSpace($errorOutput)) { 271 | $errString = "$($FunctionName) -- $($errorOutput)" 272 | if($ThrowOnFail.IsPresent) { 273 | throw $errString 274 | } 275 | else { 276 | Write-Error $errString 277 | } 278 | } 279 | } 280 | 281 | function ConvertResponseFromJsonEx { 282 | param ( 283 | [parameter(Mandatory=$false)] 284 | [string] $JsonInput 285 | ) 286 | 287 | $output = ""; 288 | if ($JsonInput) { 289 | try { 290 | $output = ($JsonInput | ConvertFrom-Json); 291 | } catch { 292 | Write-Error $_.Exception.Message 293 | return "" 294 | } 295 | if ($output.Error) { 296 | Write-Error $output; 297 | } 298 | } 299 | 300 | return $output; 301 | } 302 | 303 | -------------------------------------------------------------------------------- /scripts/OutConsoleAndLog.psm1: -------------------------------------------------------------------------------- 1 | Function Out-ConsoleAndLog { 2 | <# 3 | .SYNOPSIS 4 | Writes the specifiec message to the specified log file, and to the output stream specified by -Type. 5 | .DESCRIPTION 6 | Logs to the file specified in in the -LogFile parameter, and to the output stream selected by the 7 | -Type parameter. 8 | Log entries will be pre-pended with a time stamp. 9 | If -Type is not specified, the message is only logged. 10 | If the -Verbose switch is provided (or if the $VerbosePreference is set to 'Continue') the function 11 | also writes the message to verbose output. 12 | If the global variable 'GlobalLog' is defined, the path contained in that variable will be used as the 13 | target for the message. 14 | .PARAMETER Message 15 | Mandatory parameter, accepts pipeline input. 16 | Text string to send to log file and verbose output. 17 | .PARAMETER LogFile 18 | Optional parameter. 19 | Full path to the log file to which to write output. If the LogFile is not specified, the path 20 | specified in the global variable 'globalLog' will be used instead. If 'globalLog' is not set, 21 | then the message will not be logged. 22 | .PARAMETER Type 23 | Optional parameter. Specifies the type of console output to which to send the message. 24 | Valid choices are "Verbose", "Host", "StdOut", "Warning", and "Error". 25 | - Verbose: Writes to the PowerShell Verbose output stream. 26 | Use the -Verbose parameter or set the $VerbosePreference variable to display the Verbose stream. 27 | - StdOut (or 'Pipeline'): Writes to Standard Output. 'Pipeline' is maintained as an alias for 28 | backward compatibility 29 | - Host: Writes to the PowerShell host stream. 30 | NOTE: This is not the same as standard out. 'Host' output cannot be used in a pipeline. 31 | - Warning: Writes to the PowerShell Warning output stream 32 | - Error: Writes to the PowerShell error object. This option also throws a terminating error, 33 | If no choice is specified, Verbose will be used (StdOut would be more logical, but we use Verbose 34 | to reduce the chance of unwanted standard output causing SCCM detection failures). 35 | .PARAMETER Color 36 | Specifies the text foreground color to be used with the output type 'Host'. If any other output 37 | type is specified, this parameter will be ignored. 38 | .EXAMPLE 39 | PS> "Sending Faxes!" | Out-ConsoleAndLog -LogFile 'LikeABoss.txt' -Verbose 40 | Writes "Sending Faxes!" to the log file "LikeABoss.txt", and sends the same text to Verbose 41 | output. Demonstrates the use of pipeline input. 42 | .EXAMPLE 43 | PS> $ErrorActionPreference = 'Continue'; 44 | PS> Out-ConsoleAndLog -Message 'Creating Synergies!' -LogFile 'LikeABoss.txt' 45 | Writes "Creating Synergies" to the log file "LikeABoss.txt", and sends the same text to Verbose 46 | output. Demonstrates use of the variable $ErrorActionPreference to control verbose output. 47 | .EXAMPLE 48 | PS> Out-ConsoleAndLog -Type Warning -Message "No promotion!" -LogFile "LikeABoss.txt" 49 | Writes "No promotion!" to the warning output stream, a logs to "LikeABoss.txt" 50 | #> 51 | [cmdletBinding()] 52 | 53 | param( 54 | [parameter(Position=0,Mandatory=$True,ValueFromPipeline=$True)] 55 | [string]$Message, 56 | [parameter()] 57 | [string]$LogFile = $global:GlobalLog, 58 | [parameter()][ValidateSet('Verbose','Warning','Error','Pipeline','StdOut','Host')] 59 | [string]$Type = "Host", 60 | [parameter()][ValidateSet( 61 | 'Black', 'DarkBlue', 'DarkGreen', 'DarkCyan', 'DarkRed', 'DarkMagenta', 'DarkYellow', 62 | 'Gray', 'DarkGray', 'Blue', 'Green', 'Cyan', 'Red', 'Magenta', 'Yellow', 'White')] 63 | [string]$Color, 64 | [parameter(Mandatory=$False)] 65 | [switch]$NoDate 66 | ) 67 | 68 | Process { 69 | if (-not $NoDate) { 70 | $Message = $Type + ': [' + (get-date -Format 'yyyy-MM-dd : HH:mm:ss') + '] : ' + $Message 71 | } 72 | switch ($Type) { 73 | ('Error') {Write-Error $Message -ErrorAction Continue} 74 | ('Warning') {Write-Warning $Message} 75 | ('StdOut' -or 'Pipeline') {Write-Output $Message} 76 | ('Host') {if ($color) {Write-Host $message -Foregroundcolor $color} else {Write-Host $Message}} 77 | ('Verbose') {Write-Verbose $Message} 78 | } 79 | if ($LogFile) {$Message | Out-File -FilePath $LogFile -Append} 80 | } 81 | } 82 | 83 | Export-ModuleMember -Function Out-ConsoleAndLog -------------------------------------------------------------------------------- /scripts/Register-DeterministicNetwork.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Creates or Re-creates the WSL private network with the specified (deterministic) 4 | network range. 5 | .DESCRIPTION 6 | Normally, Hyper-V/WSL uses a collision-avoidance algorithm when assigning private 7 | network ranges to the virtual network that it creates for Hyper-V based networks. 8 | This is fine for many use cases, but remote and roaming users on corporate networks 9 | may find this behavior unacceptable as the network that Windows thought was 10 | non-conflicting at system startup may become conflicting when you later start a VPN 11 | connection to your business network. 12 | 13 | This script allows you to specify a deterministic network range and gatweway to use 14 | for WSL (functional) or Hyper-V (experimental). The network will be re-created on 15 | each startup to ensure continuity. 16 | #> 17 | [CmdletBinding()] 18 | param ( 19 | # Type of virtual network that will be configured. Supported options are "WSL" or "Hyper-V". 20 | [Parameter()] 21 | [ValidateSet("WSL", "Hyper-V")] 22 | [string]$NetworkType = "WSL", 23 | 24 | # IP address for the adapter (This address will service as the gateway address for the network.) 25 | [Parameter(Mandatory=$true)] 26 | [IPAddress]$GatewayAddress, 27 | 28 | # Address and mask bits for the network, in CIDR notation. 29 | [Parameter(Mandatory=$true)] 30 | [string]$NetworkAddress 31 | ) 32 | 33 | # Establish current path and logging: 34 | $CurrentPath = Split-Path $script:MyInvocation.MyCommand.Path -Parent 35 | $global:GlobalLog = (Join-Path -Path $CurrentPath -ChildPath "Register-$NetworkType-Network.log") 36 | if (Test-Path $GlobalLog) { Remove-Item -Path $GlobalLog -Force -Confirm:$false } 37 | 38 | # Load our custom logging module: 39 | Import-Module (Join-Path -Path $CurrentPath -ChildPath "OutConsoleAndLog.psm1") 40 | 41 | # Load the HCN script with custom HNSNetwork functions. There is an "HCN" module available 42 | # In the PowerShell gallery, upon which these functions are based, but it does not allow 43 | # us to set the NetworkID of the network, which is necessary for the fixed WSL and Hyper-V nets. 44 | # See: 45 | . (Join-Path -Path $CurrentPath -ChildPath "HCN.ps1") -ea Stop 46 | 47 | switch ($NetworkType) { 48 | "WSL" { 49 | [string] $HnsName = "WSL" 50 | # These GUID values are used consistently when Windows generates the WSL network. 51 | # I don't think there is anything magical about the values, but I am preserving 52 | # them here is case there is some reason that the Windows devs re-use the GUIDs. 53 | [guid] $HnsNetworkId = "B95D0C5E-57D4-412B-B571-18A81A16E005" 54 | [guid] $ParentID = "21894F4E-9F9C-41B5-B8EF-87948943C15E" 55 | [guid] $ChildID = "28D2ABF3-7D0A-45E8-9954-62E2D24269A6" 56 | [string] $Flags = 9 57 | # [string] $MacPoolStart = "00-15-5D-9B-F0-00" 58 | # [string] $MacPoolEnd = "00-15-5D-9B-FF-FF" 59 | # [string] $MaxEndpoints = 1 60 | $ExtraEntries = @" 61 | 62 | "IsolateSwitch": true, 63 | "@ 64 | # Auto-created WSL Switch also has: 65 | # GatewayMac : 00-15-5D-9B-F6-0B 66 | # LayeredOn : IGNORE - it changes on every reboot! 67 | # NatName : IGNORE - it changes on every reboot! 68 | # Extensions : {@{Id=E7C3B2F0-F3C5-48DF-AF2B-10FED6D72E7A; IsEnabled=False; Name=Microsoft Windows Filtering 69 | # Platform}, @{Id=EA509342-793C-4020-A3E7-9C0928454D89; IsEnabled=False; Name=Microsoft 70 | # Defender Application Guard Filter Driver}, @{Id=E9B59CFA-2BE1-4B21-828F-B6FBDBDDC017; 71 | # IsEnabled=False; Name=Microsoft Azure VFP Switch Extension}, 72 | # @{Id=430BDADD-BAB0-41AB-A369-94B67FA5BE0A; IsEnabled=True; Name=Microsoft NDIS Capture}} 73 | } 74 | "Hyper-V" { 75 | [string] $HnsName = "Default Switch" 76 | # These GUID values are used consistently when Windows generates the Hyper-V network. 77 | # I don't think there is anything magical about the values, but I am preserving 78 | # them here is case there is some reason that the Windows devs re-use the GUIDs. 79 | [guid] $HnsNetworkId = "C08CB7B8-9B3C-408E-8E30-5E16A3AEB444" 80 | [guid] $ParentID = "B81F1F65-3F5A-4789-962F-009DBC86F1C8" 81 | [guid] $ChildID = "2723DF08-8F13-4408-B2D9-F8AF6FE00592" 82 | [string] $Flags = 11 83 | # [string] $MacPoolStart = "00-15-5D-17-30-00" 84 | # [string] $MacPoolEnd = "00-15-5D-17-3F-FF" 85 | # [string] $MaxEndpoints = 0 86 | $ExtraEntries = @" 87 | 88 | "SwitchName": "$HnsName", 89 | "SwitchGuid": "$HnsNetworkId", 90 | "@ 91 | # Auto-created Default Switch also has: 92 | # NatName: Ignore this, it changes on each reboot. 93 | # GatewayMac : 00-15-5D-01-3E-00 94 | # Extensions : {@{Id=E7C3B2F0-F3C5-48DF-AF2B-10FED6D72E7A; IsEnabled=False; Name=Microsoft Windows Filtering 95 | # Platform}, @{Id=EA509342-793C-4020-A3E7-9C0928454D89; IsEnabled=True; Name=Microsoft Defender 96 | # Application Guard Filter Driver}, @{Id=E9B59CFA-2BE1-4B21-828F-B6FBDBDDC017; IsEnabled=False; 97 | # Name=Microsoft Azure VFP Switch Extension}, @{Id=430BDADD-BAB0-41AB-A369-94B67FA5BE0A; 98 | } 99 | Default { exit 100 } 100 | } 101 | 102 | # Let's try using fixed GUIDs instead... 103 | # $Guid1 = New-Guid 104 | # $Guid2 = New-Guid 105 | # $HnsNetworkConfig is the configuration block for the new HNS network for WSL. 106 | # Note: For NAT, use Flags = 0 and Type = NAT. 107 | 108 | # Original values used by this script. Biswaprio Nath advises that not all values are required. 109 | # $HnsNetworkConfig = @" 110 | # { 111 | # "Name" : "$HnsName", 112 | # "Flags": $Flags, 113 | # "Type": "ICS", 114 | # "IPv6": false, 115 | # "MaxConcurrentEndpoints": $MaxEndpoints, 116 | # "Subnets" : [ 117 | # { 118 | # "ID" : "$ParentID", 119 | # "ObjectType": 5, 120 | # "AddressPrefix" : "$NetworkAddress", 121 | # "GatewayAddress" : "$GatewayAddress", 122 | # "IpSubnets" : [ 123 | # { 124 | # "ID" : "$ChildID", 125 | # "Flags": 3, 126 | # "IpAddressPrefix": "$NetworkAddress", 127 | # "ObjectType": 6 128 | # } 129 | # ] 130 | # } 131 | # ], 132 | # "MacPools": [ 133 | # { 134 | # "EndMacAddress": "$MacPoolEnd", 135 | # "StartMacAddress": "$MacPoolStart" 136 | # } 137 | # ],$ExtraEntries 138 | # "DNSServerList" : "$GatewayAddress" 139 | # } 140 | # "@ 141 | 142 | $HnsNetworkConfig = @" 143 | { 144 | "Name" : "$HnsName", 145 | "Flags": $Flags, 146 | "Type": "ICS", 147 | "Subnets" : [ 148 | { 149 | "ID" : "$ParentID", 150 | "AddressPrefix" : "$NetworkAddress", 151 | "IpSubnets" : [ 152 | { 153 | "ID" : "$ChildID", 154 | "Flags": 3, 155 | "IpAddressPrefix": "$NetworkAddress" 156 | } 157 | ] 158 | } 159 | ],$ExtraEntries 160 | "DNSServerList" : "$GatewayAddress" 161 | } 162 | "@ 163 | 164 | # Remove any existing HNS network: 165 | $oldNet = Get-HnsNetworkEx -Id $HnsNetworkId -ea SilentlyContinue 166 | if ($oldNet) {Remove-HnsNetworkEx $oldNet} 167 | Out-ConsoleAndLog "Network Configuration to Create:" 168 | Out-ConsoleAndLog $HnsNetworkConfig 169 | 170 | # Create a new network: 171 | New-HnsNetworkEx -Id $HnsNetworkId -JsonString $HnsNetworkConfig | Out-Null 172 | 173 | # Check on the resulting adapter: 174 | $newAdapter = Get-NetAdapter -Name "vEthernet ($HnsName)" 175 | 176 | if ($newAdapter) { 177 | # Out-ConsoleAndLog "Pausing while the adapter initializes..." 178 | # start-sleep -Seconds 5 179 | if ($newAdapter.Status -eq "Up") { 180 | Out-ConsoleAndLog "$NetworkType adapter enabled." 181 | $AddressArray = @() 182 | $AddressArray += Get-NetIPAddress -InterfaceIndex $newAdapter.InterfaceIndex 183 | if ($GatewayAddress -in ($AddressArray).IPAddress) { 184 | Out-ConsoleAndLog "$NetworkType adapter has the intended IP Address." 185 | } else { 186 | Out-ConsoleAndLog "$NetworkType adapter is not configured correctly." 187 | exit 101 188 | } 189 | } else { 190 | Out-ConsoleAndLog "$NetworkName adapter is not enabled. This may not be important yet." 191 | } 192 | } else { 193 | Out-ConsoleAndLog "WSL network adapter is not present. Somthing went wrong." 194 | exit 100 195 | } 196 | 197 | Out-ConsoleAndLog "Current IPs of adapter: " 198 | foreach ($ip in $AddressArray) { 199 | Out-ConsoleAndLog $ip.ToString() 200 | } 201 | 202 | -------------------------------------------------------------------------------- /scripts/sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name" : "WSL", 3 | "Flags": 9, 4 | "Type": "ICS", 5 | "IPv6": false, 6 | "IsolateSwitch": true, 7 | "MaxConcurrentEndpoints": 1, 8 | "Subnets" : [ 9 | { 10 | "ID" : "33880104-63d9-4932-bc5c-ad7d2fee94bc", 11 | "ObjectType": 5, 12 | "AddressPrefix" : "192.168.100.0/24", 13 | "GatewayAddress" : "192.168.100.1", 14 | "IpSubnets" : [ 15 | { 16 | "ID" : "0be1ee74-16f9-4dc3-ac5a-aa741a348355", 17 | "Flags": 3, 18 | "IpAddressPrefix": "192.168.100.0/24", 19 | "ObjectType": 6 20 | } 21 | ] 22 | } 23 | ], 24 | "MacPools": [ 25 | { 26 | "EndMacAddress": "00-15-5D-52-CF-FF", 27 | "StartMacAddress": "00-15-5D-52-C0-00" 28 | } 29 | ], 30 | "DNSServerList" : "192.168.100.1" 31 | } -------------------------------------------------------------------------------- /tasks/login-task.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jgregmac/hyperv-fix-for-devs/c4879c87f949f466810a5e60fe336ea718515784/tasks/login-task.xml --------------------------------------------------------------------------------