-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathWsl-IpHandler.psm1
2977 lines (2432 loc) · 122 KB
/
Wsl-IpHandler.psm1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#Requires -Version 7.1
$scriptsToImport = @(
'FunctionsArgumentCompleters.ps1'
'FunctionsWslConfig.ps1'
'FunctionsHostsFile.ps1'
'FunctionsPrivateData.ps1'
'FunctionsPSElevation.ps1'
'Ini-In.ps1'
'Ini-Out.ps1'
)
$scriptsToImport | ForEach-Object {
. (Join-Path $PSScriptRoot 'Scripts\Powershell' $_ -Resolve) | Out-Null
}
Remove-Variable scriptsToImport
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
$vEthernetWsl = 'vEthernet (WSL)'
#region Debug Functions
function _@ {
$parentInvocationInfo = Get-Variable MyInvocation -Scope 1 -ValueOnly
$parentCommandName = $parentInvocationInfo.MyCommand.Name ?? $MyInvocation.MyCommand.Name
"$parentCommandName [$($MyInvocation.ScriptLineNumber)]:"
}
#endregion Debug Functions
class WslNamesGenerator : System.Management.Automation.IValidateSetValuesGenerator {
[string[]] GetValidValues() { return Get-WslInstancesNames }
}
function Install-WslIpHandler {
<#
.SYNOPSIS
Installs WSL IP Addresses handler into a specified WSL Instance
.DESCRIPTION
Installs WSL IP Addresses handler into a specified WSL Instance optionally with specific IP address within certain Subnet.
There are 2 modes of operations:
- Dynamic
- Static
To operate in the Dynamic Mode the only required parameter is WslInstanceName.
In this mode the following will happen:
@ specified WSL instance's file system:
a) a new script will be created: /usr/local/bin/wsl-iphandler.sh
b) a new startup script created: /etc/profile.d/run-wsl-iphandler.sh. This actually start script in a).
c) sudo permission will be created at: /etc/sudoers.d/wsl-iphandler to enable passwordless start of script a).
d) /etc/wsl-iphandler.conf will be modified to store Host names / IP offset
@ Windows host file system:
a) New [ip_offsets] section in `~\.wsl-iphandler-config` will be created to store ip_offset for a specified WSL Instance. This offset will be used by bash startup script to create an IP Address at start time.
b) When bash startup script on WSL instance side is executed it will create (if not present already) a record binding its current IP address to it's host name (which is set by WslHostName parameter)
To operate in Static Mode at the very least one parameter has to be specified: GatewayIpAddress.
In this mode the following will happen:
@ specified WSL instance's file system:
a) the same scripts will be created as in Static Mode.
b) /etc/wsl-iphandler.conf will be modified to store Host names / IP Addresses
Note that if parameter WslInstanceIpAddress is not specified a first available IP address will be selected and will be used until the Wsl-IpHandler is Uninstalled. Otherwise specified IP address will be used.
@ Windows host file system:
a) New [static_ips] section in `~\.wsl-iphandler-config` will be created to store ip address for a specified WSL Instance. This ip address will be used by bash startup script to bind this IP Address at start time to eth0 interface.
b) The same as for Static Mode.
c) Powershell profile file (CurrentUserAllHosts) will be modified: This module will be imported and an alias `wsl` to Invoke-WslExe will be created).
.PARAMETER WslInstanceName
Required. Name of the WSL Instance as listed by `wsl.exe -l` command
.PARAMETER GatewayIpAddress
Optional. IP v4 Address of the gateway. This IP Address will appear in properties of Network Adapter (vEthernet (WSL)).
.PARAMETER PrefixLength
Optional. Defaults to 24. Length of WSL Subnet.
.PARAMETER DNSServerList
Optional. Defaults to GatewayIpAddress.
.PARAMETER WslInstanceIpAddress
Optional. Static IP Address of WSL Instance. It will be assigned to the instance when it starts. This address will be also added to Windows HOSTS file so that a given WSL Instance can be accessed via its WSLHostName.
.PARAMETER WslHostName
Optional. Defaults to WslInstanceName. The name to use to access the WSL Instance on WSL SubNet. This name together with WslInstanceIpAddress are added to Windows HOSTS file.
.PARAMETER WindowsHostName
Optional. Defaults to `windows`. Name of Windows Host that can be used to access windows host from WSL Instance. This will be added to /etc/hosts on WSL Instance system.
.PARAMETER DontModifyPsProfile
Optional. If specifies will not modify Powershell Profile (default profile: CurrentUserAllHosts). Otherwise profile will be modified to Import this module and create an Alias `wsl` which will transparently pass through any and all paramaters to `wsl.exe` and, if necessary, initialize beforehand WSL Hyper-V network adapter to allow usage of Static IP Addresses. Will be ignored in Dynamic Mode.
.PARAMETER UseScheduledTaskOnUserLogOn
When this parameter is present - a new Scheduled Task will be created: Wsl-IpHandlerTask. It will be triggered at user LogOn. This task execution is equivalent to running Set-WslNetworkAdapter command. It will create WSL Hyper-V Network Adapter when user Logs On.
.PARAMETER AnyUserLogOn
When this parameter is present - The Scheduled Task will be set to run when any user logs on. Otherwise (default behavior) - the task will run only when current user (who executed Install-WslIpHandler command) logs on.
.PARAMETER BackupWslConfig
Optional. If specified will create backup of `~\.wsl-iphandler-config` and `~\.wslconfig` before modifications.
.PARAMETER NoSwapCheck
When this parameter is present there will be no checking whether WSL2 is configured to use swap file which is compressed. Compressed swap file is a known cause of networking issue in WSL2. It is not recommended to use this parameter.
.PARAMETER NoNetworkShareCheck
When this parameter is present there will be no checking whether Wsl-IpHandler module is installed on a network share. WSL2 does not yet support windows network shares within linux so when the module is installed on a network share it will NOT be working correctly. It is not recommended to use this parameter.
.PARAMETER AutoFixWslConfig
Wsl-IpHandler module requires the default configuration settings in windows ~/.wslconfig and in linux /etc/wsl.conf files. When these files are modified by the user the module might not work correctly. When this parameter is present any misconfiguration that will cause the module to NOT work correctly will be automatically fixed (i.e. default settings will be restored).
Specifically the following settings are checked:
- in ~/.wslconfig:
[wsl2]
swap = ...
- in /etc/wsl.conf:
[interop]
enabled = true
appendWindowsPath = true
[automount]
enabled = true
[network]
generateResolvConf = true
.PARAMETER DynamicAdapters
Array of strings - names of Hyper-V Network Adapters that can be moved to other IP network space to free space for WSL adapter. Defaults to: `'Ethernet', 'Default Switch'`
.EXAMPLE
------------------------------------------------------------------------------------------------
Install-WslIpHandler -WslInstanceName Ubuntu
Will install WSL IP Handler in Dynamic Mode. IP address of WSL Instance will be set when the instance starts and address will be based on whatever SubNet will be set by Windows system.
This IP address might be different after Windows restarts as it depends on what Gateway IP address Windows assigns to vEthernet (WSL) network adapter.
The actual IP address of WSL Instance can always be checked with command: `hostname -I`.
------------------------------------------------------------------------------------------------
Install-WslIpHandler -WslInstanceName Ubuntu -GatewayIpAddress 172.16.0.1
Will install WSL IP Handler in Static Mode. IP address of WSL Instance will be set automatically to the first available in SubNet 172.16.0.0/24, excluding Gateway IP address.
From WSL Instance shell prompt Windows Host will be accessible at 172.16.0.1 or simply as `windows`, i.e. two below commands will yield the same result:
ping 172.16.0.1
ping windows
------------------------------------------------------------------------------------------------
Install-WslIpHandler -WslInstanceName Ubuntu -GatewayIpAddress 172.16.0.1 -WslInstanceIpAddress 172.16.0.2
Will install WSL IP Handler in Static Mode. IP address of WSL Instance will be set to 172.16.0.2. This IP Address will stay the same as long as wsl instance is started through this module's alias `wsl` (which shadows `wsl` command) and until Uninstall-WslIpHandler is executed.
.NOTES
Use Powershell command prompt to launch WSL Instance(s) in Static Mode, especially after system restart.
Executing `wsl.exe` from within Windows cmd.exe after Windows restarts will allow Windows to take control over WSL network setup and will break Static IP functionality.
To mannerly take control over WSL Network setup use this module's command: Set-WslNetworkConfig
#>
param (
[Parameter(Mandatory, Position = 0, ParameterSetName = 'Dynamic')]
[Parameter(Mandatory, Position = 0, ParameterSetName = 'Static')]
[ValidateSet([WslNamesGenerator], ErrorMessage = "'{0}' is not one of the installed distributions: {1}")]
[Alias('Name')]
[string]$WslInstanceName,
[Parameter(Mandatory, ParameterSetName = 'Static')][Alias('Gateway')]
[ipaddress]$GatewayIpAddress,
[Parameter(ParameterSetName = 'Static')][Alias('Prefix')]
[int]$PrefixLength = 24,
[Parameter(ParameterSetName = 'Static')][Alias('DNS')]
[string]$DNSServerList, # String with Comma separated ipaddresses/hosts
[Parameter(ParameterSetName = 'Static')][Alias('IpAddress')]
[ipaddress]$WslInstanceIpAddress,
[Parameter()]
[ValidateNotNullOrEmpty()]
[string]$WslHostName = $WslInstanceName,
[Parameter()]
[string]$WindowsHostName = 'windows',
# [Parameter(ParameterSetName = 'Static')]
[Parameter()]
[Alias('NoProfile')]
[switch]$DontModifyPsProfile,
[Parameter(ParameterSetName = 'Static')]
[Alias('Logon')]
[Alias('Task')]
[switch]$UseScheduledTaskOnUserLogOn,
[Parameter(ParameterSetName = 'Static')]
[Alias('AnyUser')]
[switch]$AnyUserLogOn,
[Parameter()]
[Alias('Backup')]
[switch]$BackupWslConfig,
[Parameter()]
[Alias('IgnoreSwap')]
[switch]$NoSwapCheck,
[Parameter()]
[Alias('IgnoreNetworkShare')]
[switch]$NoNetworkShareCheck,
[Parameter()]
[Alias('Fix')]
[switch]$AutoFixWslConfig,
[Parameter(ParameterSetName = 'Static')]
[string[]]$DynamicAdapters = @('Ethernet', 'Default Switch')
)
Write-Host "PowerShell installing Wsl-IpHandler to $WslInstanceName..."
Write-Debug "$(_@) `$PSBoundParameters: $(& {$args} @PSBoundParameters)"
$wslConfigModified = $false
$moduleConfigModified = $false
if (-not $NoNetworkShareCheck) { Test-ModuleOnNetworkShareAndPrompt }
if (-not $NoSwapCheck) {
Test-SwapAndPrompt -Modified ([ref]$wslConfigModified) -AutoFix:$AutoFixWslConfig
}
Test-EtcWslConfAndPrompt -WslInstanceName $WslInstanceName -AutoFix:$AutoFixWslConfig
#region Save Network Parameters to config file and Setup Network Adapters
Set-WslNetworkConfig -GatewayIpAddress $GatewayIpAddress -PrefixLength $PrefixLength -DNSServerList $DNSServerList -DynamicAdapters $DynamicAdapters -WindowsHostName $WindowsHostName -Modified ([ref]$moduleConfigModified)
if ($null -ne $GatewayIpAddress) {
Write-Debug "$(_@) `$PSBoundParameters: $(& {$args} @PSBoundParameters)"
$setParams = @{
GatewayIpAddress = $GatewayIpAddress
PrefixLength = $PrefixLength
DNSServerList = $DNSServerList
DynamicAdapters = $DynamicAdapters
WaitForWslNetworkConnection = $true
}
Set-WslNetworkAdapter @setParams -Verbose:$($VerbosePreference -eq 'Continue')
Write-Verbose "Setting Static IP Address: $WslInstanceIpAddress for $WslInstanceName."
Set-WslInstanceStaticIpAddress -WslInstanceName $WslInstanceName -GatewayIpAddress $GatewayIpAddress -PrefixLength $PrefixLength -WslInstanceIpAddress $WslInstanceIpAddress -Modified ([ref]$moduleConfigModified)
if ($UseScheduledTaskOnUserLogOn) {
Write-Verbose 'Registering Wsl-IpHandler scheduled task...'
$taskParams = @{
WaitForWslNetworkConnection = $true
ShowToast = $true
AnyUserLogOn = $AnyUserLogOn
}
Set-WslScheduledTask @taskParams
}
}
else {
$WslIpOffset = Get-WslConfigIpOffset $WslInstanceName
if (-not $WslIpOffset) {
$WslIpOffset = Get-WslConfigAvailableIpOffset
Write-Verbose "Setting Automatic IP Offset: $WslIpOffset for $WslInstanceName."
Set-WslConfigIpOffset $WslInstanceName $WslIpOffset -Modified ([ref]$moduleConfigModified)
}
}
if ($wslConfigModified) {
Write-Verbose "Saving Configuration in config file$($BackupWslConfig ? ' with Backup ' : '')..."
Write-WslConfig Wsl -Backup:$BackupWslConfig
}
if ($moduleConfigModified) {
Write-Verbose "Saving Configuration in .wsl-iphandler-config$($BackupWslConfig ? ' with Backup ' : '')..."
Write-WslConfig WslIpHandler -Backup:$BackupWslConfig
}
#endregion Save Network Parameters to config file and Setup Network Adapters
#region Bash Scripts Installation
Install-WslBashScripts -WslInstanceName $WslInstanceName -WslHostName $WslHostName
#endregion Bash Scripts Installation
#region Set Content to Powershell Profile
if (-not $DontModifyPsProfile) {
Write-Verbose "Modifying Powershell Profile: $($profile.CurrentUserAllHosts) ..."
Set-ProfileContent
}
#endregion Set Content to Powershell Profile
#region Restart WSL Instance
Write-Verbose "Terminating running instances of $WslInstanceName ..."
wsl.exe -t $WslInstanceName | Out-Null
#endregion Restart WSL Instance
#region Test IP and host Assignments
try {
Write-Verbose "Testing Activation of WSL IP Handler on $WslInstanceName ..."
Test-WslInstallation -WslInstanceName $WslInstanceName -WslHostName $WslHostName -WindowsHostName $WindowsHostName
}
catch {
Write-Host "PowerShell finished installation of Wsl-IpHandler to $WslInstanceName with Errors:"
Write-Debug "$(_@) ScriptStackTrace: $($_.ScriptStackTrace)"
Write-Host "$_" -ForegroundColor Red
return
}
finally {
Write-Verbose 'Finished Testing Activation of WSL IP Handler.'
wsl.exe -t $WslInstanceName | Out-Null
}
#endregion Test IP and host Assignments
Write-Host "PowerShell successfully installed Wsl-IpHandler to $WslInstanceName."
}
function Uninstall-WslIpHandler {
<#
.SYNOPSIS
Uninstall WSL IP Handler from WSL Instance
.DESCRIPTION
Uninstall WSL IP Handler from WSL Instance with the specified name.
.PARAMETER WslInstanceName
Required. Name of the WSL Instance to Uninstall WSL Handler from (should be one of the names listed by `wsl.exe -l` command).
.PARAMETER BackupWslConfig
Optional. If specified config files will backed up before modifications.
.EXAMPLE
Uninstall-WslIpHandler -WslInstanceName Ubuntu
.NOTES
When the instance specified in WslInstanceName parameter is the LAST one (there are no other instances for which static IP address has been assigned) This command will also reset
#>
param (
[Parameter(Mandatory)]
[ValidateSet([WslNamesGenerator],
ErrorMessage = "'{0}' is not one of the installed distributions: {1}" )]
[Alias('Name')]
[string]$WslInstanceName,
[switch]$BackupWslConfig
)
Write-Host "PowerShell Uninstalling Wsl-IpHandler from $WslInstanceName..."
#region Bash Scripts UnInstallation
Uninstall-WslBashScripts -WslInstanceName $WslInstanceName
#endregion Bash Scripts UnInstallation
#region Restart WSL Instance
wsl.exe -t $WslInstanceName | Out-Null
Write-Debug "$(_@) Restarted $WslInstanceName"
#endregion Restart WSL Instance
#region Remove WSL Instance Static IP from config file
$moduleConfigModified = $false
Remove-WslInstanceStaticIpAddress -WslInstanceName $WslInstanceName -Modified ([ref]$moduleConfigModified)
#endregion Remove WSL Instance Static IP from config file
#region Remove WSL Instance IP Offset from config file
Write-Debug "$(_@) Removing IP address offset for $WslInstanceName from config file..."
Remove-WslConfigIpOffset -WslInstanceName $WslInstanceName -Modified ([ref]$moduleConfigModified)
#endregion Remove WSL Instance IP Offset from config file
#region Remove WSL Instance IP from windows hosts file
$hostsModified = $false
Write-Debug "$(_@) Removing record for $WslInstanceName from Windows Hosts ..."
$content = (Get-HostsFileContent)
Write-Debug "$(_@) Removing Host: $WslInstanceName from $($content.Count) Windows Hosts records ..."
$content = Remove-HostFromRecords -Records $content -HostName $WslInstanceName -Modified ([ref]$hostsModified)
Write-Debug "$(_@) Setting Windows Hosts file with $($content.Count) records ..."
#endregion Remove WSL Instance IP from windows hosts file
#region Save Modified config and hosts Files
if ($hostsModified) { Write-HostsFileContent -Records $content }
#endregion Save Modified config and hosts Files
#region Remove Network Config and Content from Powershell Profile and ScheduledTask
# Clean config file, PSProfile and ScheduledTask if there are no more Static IP assignments
$wslConfigHasNoIpAssignments = Test-WslConfigIsSafeToDelete -ExcludeNetworkSection
if ($wslConfigHasNoIpAssignments) {
Write-Debug "$(_@) No Static IPs and no IP offsets found in config file"
#region Remove WSL Network Configuration from config file
Write-Debug "$(_@) Removing WSL Network Configuration for $WslInstanceName ..."
Remove-WslNetworkConfig -Modified ([ref]$moduleConfigModified)
#endregion Remove WSL Network Configuration from config file
Write-Debug "$(_@) Removing Powershell Profile Modifications ..."
Remove-ProfileContent
Write-Debug "$(_@) Removing Scheduled Task ..."
Remove-WslScheduledTask -CheckSuccess
}
else {
Write-Debug "$(_@) Skipping Removal of Network Config, Powershell Profile modifications and ScheduledTaskThere because there are Static IPs or IP offsets remaining:"
Write-Debug "$(_@) $(Get-WslConfigStaticIpSection | Out-String)"
}
if ($moduleConfigModified) { Write-WslConfig WslIpHandler -Backup:$BackupWslConfig }
#endregion Remove Network Config and Content from Powershell Profile and ScheduledTask
if (Test-WslConfigIsSafeToDelete) {
Write-Verbose "$(_@) Deleting module config file..."
Remove-ModuleConfigFile -Backup:$BackupWslConfig
}
Write-Host "PowerShell successfully uninstalled Wsl-IpHandler from $WslInstanceName!"
}
function Install-WslBashScripts {
<#
.SYNOPSIS
Installs (copies) Bash scripts to specified WSL Instance
.DESCRIPTION
Installs (copies) Bash scripts to specified WSL Instance to enable IP address determinism.
.PARAMETER WslInstanceName
Required. Name of the WSL Instance as listed by `wsl.exe -l` command
.PARAMETER WslHostIpOrOffset
Required. Either Static IP Address
.PARAMETER WslHostName
Optional. Defaults to WslInstanceName. The name to use to access the WSL Instance on WSL SubNet. This name together with WslInstanceIpAddress are added to Windows HOSTS file.
.EXAMPLE
Install-WslBashScripts -WslInstanceName Ubuntu
Will read saved configuration from config file and use this setting to apply to Ubuntu WSL Instance.
.NOTES
This command can only be used AFTER `Install-WslIpHandler` command was used to setup network configuration.
#>
param(
[Parameter(Mandatory, Position = 0)]
[ValidateSet([WslNamesGenerator],
ErrorMessage = "'{0}' is not one of the installed distributions: {1}" )]
[Alias('Name')]
[string]$WslInstanceName,
[Parameter()]
[ValidateNotNullOrEmpty()]
[string]$WslHostName = $WslInstanceName,
[Parameter()]
[switch]$BashVerbose,
[Parameter()]
[switch]$BashDebug
)
#region Read WSL config
Read-WslConfig -ConfigType 'WslIpHandler' | Out-Null
$existingIp = Get-WslConfigStaticIpAddress -WslInstanceName $WslInstanceName
if ($existingIp) {
Write-Debug "$(_@) Existing Static IP Address for $WslInstanceName = $existingIp"
$WslHostIpOrOffset = "$existingIp"
}
else {
Write-Debug "$(_@) Static IP Address for $WslInstanceName not found Querying for Offset."
$WslIpOffset = Get-WslConfigIpOffset $WslInstanceName
if ($WslIpOffset) {
$WslHostIpOrOffset = $WslIpOffset
}
else {
$msg = "Neither Static IP Address nor IP Offset are configured for '$WslInstanceName'. Please run `Install-WslIpHandler` to set either Static IP Address or Offset before using this command."
Throw "$msg"
}
}
$WindowsHostName = Get-WslConfigWindowsHostName -DefaultValue 'windows'
Write-Debug "$(_@) `$WindowsHostName='$WindowsHostName'"
#endregion Read WSL config
#region Bash Installation Script Path
$BashInstallScript = Get-SourcePath 'BashInstall'
Write-Debug "$(_@) `$BashInstallScript='$BashInstallScript'"
#endregion Bash Installation Script Path
#region Module's Bash Config Path
$BashConfigPath = Get-BashConfigFilePath 'WslIpHandler'
#endregion Module's Bash Config Path
#region WSL Autorun Script Path
# Get Path to bash script that assigns IP to wsl instance and launches PS autorun script
$BashAutorunScriptSource = Get-SourcePath 'BashAutorun'
Write-Debug "$(_@) `$BashAutorunScriptSource='$BashAutorunScriptSource'"
$BashAutorunScriptTarget = Get-ScriptLocation 'BashAutorun'
Write-Debug "$(_@) `$BashAutorunScriptTarget='$BashAutorunScriptTarget'"
#endregion WSL Autorun Script Path
#region PS Autorun
# Get Path to PS Script that injects (if needed) IP-host to windows hosts on every WSL launch
$WinHostsEditScript = Get-SourcePath 'WinHostsEdit'
Write-Debug "$(_@) `$WinHostsEditScript='$WinHostsEditScript'"
#endregion PS Autorun
#region Run Bash Installation Script
Write-Verbose "Running Bash WSL Install script $BashInstallScript"
Write-Debug "$(_@) `$DebugPreference=$DebugPreference"
Write-Debug "$(_@) `$VerbosePreference=$VerbosePreference"
$bashInstallScriptWslPath = "`"`$(wslpath '$BashInstallScript')`""
$bashCommand = @(
$bashInstallScriptWslPath
"'$BashAutorunScriptSource'"
"'$BashAutorunScriptTarget'"
"'$WinHostsEditScript'"
"'$BashConfigPath'"
"$WindowsHostName"
"$WslHostName"
"$WslHostIpOrOffset"
) -join ' '
$envVars = @()
if ($DebugPreference -gt 0) { $envVars += 'DEBUG=1' }
if ($VerbosePreference -gt 0) { $envVars += 'VERBOSE=1' }
$bashArgs = @()
if ($BashVerbose) { $bashArgs += '--verbose' }
if ($BashDebug) { $bashArgs += '--debug' }
Write-Debug "$(_@) Invoking: wsl.exe -d $WslInstanceName -e sudo -E env $envVars bash $bashArgs -c $bashCommand"
$bashInstallScriptOutput = wsl.exe -d $WslInstanceName -e sudo -E env @envVars bash @bashArgs -c $bashCommand
if ($DebugPreference -gt 0 -or $VerbosePreference -gt 0) {
Write-Host "$($bashInstallScriptOutput -join "`n")"
}
if ($bashInstallScriptOutput -and ($bashInstallScriptOutput | Where-Object { $_.StartsWith('[Error') } | Measure-Object).Count -gt 0) {
Write-Error "Error(s) occurred while running Bash Installation script: $bashCommand`n$($bashInstallScriptOutput -join "`n")"
return
}
Write-Debug "$(_@) Installed Bash scripts."
Write-Verbose 'Installed Bash scripts.'
#endregion Run Bash Installation Script
}
function Uninstall-WslBashScripts {
<#
.SYNOPSIS
Uninstalls (deletes) Bash scripts from specified WSL Instance.
.DESCRIPTION
Uninstalls (deletes) Bash scripts and removes all modifications made by this module to the specified WSL Instance.
.PARAMETER WslInstanceName
Required. Name of the WSL Instance as listed by `wsl.exe -l` command
.EXAMPLE
Uninstall-WslBashScripts Ubuntu
Will remove all Bash scripts and all modifications made to Ubuntu WSL Instance.
#>
param(
[Parameter(Mandatory)]
[ValidateSet([WslNamesGenerator],
ErrorMessage = "'{0}' is not one of the installed distributions: {1}" )]
[Alias('Name')]
[string]$WslInstanceName,
[Parameter()]
[switch]$BashVerbose,
[Parameter()]
[switch]$BashDebug
)
#region Bash UnInstallation Script Path
$BashUninstallScript = Get-SourcePath 'BashUninstall'
#endregion Bash InInstallation Script Path
#region WSL Autorun
# Get Path to bash script that assigns IP to wsl instance and launches PS autorun script
$BashAutorunScriptName = Split-Path -Leaf (Get-SourcePath 'BashAutorun')
$BashAutorunScriptTarget = Get-ScriptLocation 'BashAutorun'
#endregion WSL Autorun
#region Module's Bash Config Path
$BashConfigPath = Get-BashConfigFilePath 'WslIpHandler'
#endregion Module's Bash Config Path
#region Remove Bash Autorun
Write-Verbose "Running Bash WSL Uninstall script $BashUninstallScript"
Write-Debug "$(_@) `$DebugPreference=$DebugPreference"
Write-Debug "$(_@) `$VerbosePreference=$VerbosePreference"
$bashUninstallScriptWslPath = "`"`$(wslpath '$BashUninstallScript')`""
$bashCommand = @(
$bashUninstallScriptWslPath
$BashAutorunScriptName
$BashAutorunScriptTarget
$BashConfigPath
) -join ' '
$envVars = @()
if ($DebugPreference -gt 0) { $envVars += 'DEBUG=1' }
if ($VerbosePreference -gt 0) { $envVars += 'VERBOSE=1' }
$bashArgs = @()
if ($BashVerbose) { $bashArgs += '--verbose' }
if ($BashDebug) { $bashArgs += '--debug' }
Write-Debug "$(_@) Invoking: wsl.exe -d $WslInstanceName -e sudo -E env $envVars bash $bashArgs -c $bashCommand"
$bashUninstallScriptOutput = wsl.exe -d $WslInstanceName -e sudo -E env @envVars bash @bashArgs -c $bashCommand
if ($DebugPreference -gt 0 -or $VerbosePreference -gt 0) {
Write-Host "$($bashUninstallScriptOutput -join "`n")"
}
if ($bashUninstallScriptOutput -and ($bashUninstallScriptOutput | Where-Object { $_.StartsWith('[Error') } | Measure-Object).Count -gt 0) {
Write-Error "Error(s) occurred while running Bash Uninstall script: $bashCommand`n$($bashUninstallScriptOutput -join "`n")"
return
}
Write-Debug "$(_@) Uninstalled Bash scripts."
Write-Verbose 'Uninstalled Bash scripts.'
#endregion Remove Bash Autorun
}
function Update-WslBashScripts {
<#
.SYNOPSIS
Updates Bash scripts of specified WSL Instance to the latest version.
.DESCRIPTION
Uninstalls (deletes) and then installs (copies) last version of Bash scripts to specified WSL Instance
.PARAMETER WslInstanceName
Required. Name of the WSL Instance as listed by `wsl.exe -l` command.
.PARAMETER WslHostName
Optional. Defaults to WslInstanceName. The name to use to access the WSL Instance on WSL SubNet. This name together with WslInstanceIpAddress are added to Windows HOSTS file.
.PARAMETER All
If this switch parameter is specified all instances where Wsl-IpHandler was installed will be updated.
.EXAMPLE
Update-WslBashScripts -WslInstanceName Ubuntu
Will read saved configuration from config file and use this setting to apply to Ubuntu WSL Instance.
.NOTES
This command can only be used AFTER `Install-WslIpHandler` command was used to setup network configuration.
#>
param(
[Parameter(Mandatory, Position = 0, ParameterSetName = 'Single')]
[ValidateSet([WslNamesGenerator],
ErrorMessage = "'{0}' is not one of the installed distributions: {1}" )]
[Alias('Name')]
[string]$WslInstanceName,
[Parameter(ParameterSetName = 'Single')]
[ValidateNotNullOrEmpty()]
[string]$WslHostName = $WslInstanceName,
[Parameter(Mandatory, ParameterSetName = 'All')]
[switch]$All
)
Write-Debug "$(_@) `$PSBoundParameters: $(& {$args} @PSBoundParameters)"
if ($All) {
$status = Get-WslStatus -StaticIp -DynamicIp -PassThru -InformationAction Ignore
$wslInstances = [string[]]($status.StaticIp.Keys) + $status.DynamicIp
foreach ($instance in $wslInstances) {
$wslHostName = (Get-HostForIpAddress $instance) ?? $instance
$params = @{
WslInstanceName = $instance
WslHostName = $wslHostName
}
if ($VerbosePreference -eq 'Continue') { $params.Verbose = $true }
if ($DebugPreference -eq 'Continue') { $params.Debug = $true }
Write-Debug "$(_@) Invoking Update-WslBashScripts $(& {$args} @params)"
Update-WslBashScripts @params
}
}
#region Local Bash Script Paths
$BashUpdateScript = Get-SourcePath 'BashUpdate'
Write-Debug "$(_@) `$BashUpdateScript='$BashUpdateScript'"
$BashUninstallScript = Get-SourcePath 'BashUninstall'
Write-Debug "$(_@) `$BashUninstallScript='$BashUninstallScript'"
$BashInstallScript = Get-SourcePath 'BashInstall'
Write-Debug "$(_@) `$BashInstallScript='$BashInstallScript'"
#endregion Local Bash Script Paths
#region WSL Autorun
# Get Path to bash script that assigns IP to wsl instance and launches PS autorun script
$BashAutorunScriptSource = Get-SourcePath 'BashAutorun'
Write-Debug "$(_@) `$BashAutorunScriptSource='$BashAutorunScriptSource'"
$BashAutorunScriptName = Split-Path -Leaf (Get-SourcePath 'BashAutorun')
Write-Debug "$(_@) `$BBashAutorunScriptName='$BashAutorunScriptName'"
$BashAutorunScriptTarget = Get-ScriptLocation 'BashAutorun'
Write-Debug "$(_@) `$BashAutorunScriptTarget='$BashAutorunScriptTarget'"
#endregion WSL Autorun
#region Bash Script WSL Paths
$BashUpdateScriptWslPath = "`"`$(wslpath '$BashUpdateScript')`""
Write-Debug "$(_@) `$BashUpdateScriptWslPath='$BashUpdateScriptWslPath'"
#endregion Bash Script WSL Paths
#region Module's Bash Config Path
$BashConfigPath = Get-BashConfigFilePath 'WslIpHandler'
#endregion Module's Bash Config Path
#region Install Script Arguments
# Get Path to PS Script that injects (if needed) IP-host to windows hosts on every WSL launch
$WinHostsEditScript = Get-SourcePath 'WinHostsEdit'
Write-Debug "$(_@) `$WinHostsEditScript='$WinHostsEditScript'"
$WindowsHostName = Get-WslConfigWindowsHostName -DefaultValue 'windows'
Write-Debug "$(_@) `$WindowsHostName='$WindowsHostName'"
$existingIp = Get-WslConfigStaticIpAddress -WslInstanceName $WslInstanceName
if ($existingIp) {
Write-Debug "$(_@) Existing Static IP Address for $WslInstanceName = $existingIp"
$WslHostIpOrOffset = "$existingIp"
}
else {
Write-Debug "$(_@) Static IP Address for $WslInstanceName not found Querying for Offset."
$WslIpOffset = Get-WslConfigIpOffset $WslInstanceName
if ($WslIpOffset) {
$WslHostIpOrOffset = $WslIpOffset
}
else {
$msg = "Neither Static IP Address nor IP Offset are configured for '$WslInstanceName'. Please run `Install-WslIpHandler` to set either Static IP Address or Offset before using this command."
Throw "$msg"
}
}
#endregion Install Script Arguments
#region Run Update Script
Write-Verbose "Running Bash WSL Update script $BashUpdateScript"
Write-Debug "$(_@) `$DebugPreference=$DebugPreference"
Write-Debug "$(_@) `$VerbosePreference=$VerbosePreference"
$bashUninstallArgs = @(
"$BashAutorunScriptName"
"$BashAutorunScriptTarget"
"$BashConfigPath"
)
$bashInstallArgs = @(
"'$BashAutorunScriptSource'"
"'$BashAutorunScriptTarget'"
"'$WinHostsEditScript'"
$BashConfigPath
$WindowsHostName
$WslHostName
$WslHostIpOrOffset
)
$bashCommand = @($BashUpdateScriptWslPath)
$bashCommand = + $bashUninstallArgs
$bashCommand = + $bashInstallArgs
$bashCommand = $bashCommand -join ' '
$envVars = @()
$bashArgs = @()
if ($VerbosePreference -eq 'Continue') { $envVars += 'VERBOSE=1'; $bashArgs += '--verbose' }
if ($DebugPreference -eq 'Continue') { $envVars += 'DEBUG=1'; $bashArgs += '--debug' }
Write-Debug "$(_@) Invoking: wsl.exe -d $WslInstanceName -e sudo -E env $envVars bash $bashArgs -c $bashCommand"
$bashUpdateScriptOutput = wsl.exe -d $WslInstanceName -e sudo -E env @envVars bash @bashArgs -c $bashCommand
if ($DebugPreference -gt 0 -or $VerbosePreference -gt 0) {
Write-Host "$($bashUpdateScriptOutput -join "`n")"
}
if ($bashUpdateScriptOutput -and ($bashUpdateScriptOutput | Where-Object { $_.StartsWith('[Error') } | Measure-Object).Count -gt 0) {
Write-Error "Error(s) occurred while running Bash Update script: $bashCommand`n$($bashUpdateScriptOutput -join "`n")"
return
}
Write-Debug "$(_@) Updated Bash scripts."
Write-Verbose 'Updated Bash scripts.'
#endregion Run Update Script
}
function Set-ProfileContent {
<#
.SYNOPSIS
Modifies Powershell profile to set alias `wsl` -> Invoke-WslExe
.DESCRIPTION
Modifies Powershell profile file (by default CurrentUserAllHosts) to set alias `wsl` -> Invoke-WslExe.
.PARAMETER ProfilePath
Optional. Path to Powershell profile. Defaults to value of $Profile.CurrentUserAllhosts.
.EXAMPLE
Set-ProfileContent
Modifies the default location for CurrentUserAllhosts.
------------------------------------------------------------------------------------------------
Set-ProfileContent $Profile.AllUsersAllHosts
Modifies the system profile file.
.NOTES
Having `wsl` alias in profile allows to automatically enable WSL Network Adapter with manually setting it up before launching WSL instance.
#>
param([Parameter()]$ProfilePath = $profile.CurrentUserAllHosts)
Write-Debug "$(_@) ProfilePath: $ProfilePath"
$handlerContent = Get-ProfileContentTemplate
$content = (Get-Content -Path $ProfilePath -ErrorAction SilentlyContinue) ?? @()
$anyHandlerContentMissing = $false
foreach ($line in $handlerContent) {
if ($line -notin $content) { $anyHandlerContentMissing = $true; break }
}
if ($anyHandlerContentMissing) {
# Safeguard to avoid duplication in case user manually edits profile file
$content = $content | Where-Object { $handlerContent -notcontains $_ }
$content += $handlerContent
Set-Content -Path $ProfilePath -Value $content -Force
Write-Warning "Wsl-IpHandler Content was added to Powershell profile: $ProfilePath."
Write-Warning 'The changes will take effect after Powershell session is restarted!'
# . $ProfilePath # !!! DONT DO THAT -> IT Removes ALL Sourced functions (i.e. '. File.ps1')
}
else {
Write-Debug "$(_@) Wsl-IpHandler Content is already present in Powershell profile: $ProfilePath."
}
}
function Remove-ProfileContent {
<#
.SYNOPSIS
Removes modifications made by Set-ProfileContent command.
.DESCRIPTION
Removes modifications made by Set-ProfileContent command.
.PARAMETER ProfilePath
Optional. Path to Powershell profile. Defaults to value of $Profile.CurrentUserAllhosts.
#>
param([Parameter()]$ProfilePath = $Profile.CurrentUserAllHosts)
Write-Debug "$(_@) ProfilePath: $ProfilePath"
$handlerContent = Get-ProfileContentTemplate
$content = (Get-Content -Path $ProfilePath -ErrorAction SilentlyContinue) ?? @()
if ($content) {
$handlerContentIsPresent = $null -ne ($content | Where-Object {
$handlerContent -contains $_
})
if ($handlerContentIsPresent) {
$content = $content | Where-Object { $handlerContent -notcontains $_ }
Set-Content -Path $ProfilePath -Value $content -Force
Write-Warning "Wsl-IpHandler Content was removed from Powershell profile: $ProfilePath."
Write-Warning 'The changes will take effect after Powershell session is restarted!'
}
else {
Write-Debug "$(_@) Wsl-IpHandler Content not found in $ProfilePath"
}
}
}
function Set-WslInstanceStaticIpAddress {
<#
.SYNOPSIS
Sets Static IP Address for the specified WSL Instance.
.DESCRIPTION
Sets Static IP Address for the specified WSL Instance. Given WslInstanceIpAddress will be validated against specified GatewayIpAddress and PrefixLength, error will be thrown if it is incorrect.
.PARAMETER WslInstanceName
Required. Name of WSL Instance as listed by `wsl.exe -l` command.
.PARAMETER GatewayIpAddress
Required. Gateway IP v4 Address of vEthernet (WSL) network adapter.
.PARAMETER PrefixLength
Optional. Defaults to 24. WSL network SubNet Length.
.PARAMETER WslInstanceIpAddress
Required. IP v4 Address to assign to WSL Instance.
.PARAMETER Modified
Optional. Reference to boolean variable. Will be set to True if given parameters will lead to change of existing settings. If this parameter is specified - any occuring changes will have to be saved with Write-WslConfig command. This parameter cannot be used together with BackupWslConfig parameter.
.PARAMETER BackupWslConfig
Optional. If given - original version of config file will be saved as backup. This parameter cannot be used together with Modified parameter.
.EXAMPLE
Set-WslInstanceStaticIpAddress -WslInstanceName Ubuntu -GatewayIpAddress 172.16.0.1 -WslInstanceIpAddress 172.16.0.11
Will set Ubuntu WSL Instance Static IP address to 172.16.0.11
.NOTES
This command only checks against specified Gateway IP Address, not actual one (even if it exists). Any changes made will require restart of WSL instance for them to take effect.
#>
param (
[Parameter(Mandatory)]
[ValidateSet([WslNamesGenerator],
ErrorMessage = "'{0}' is not one of the installed distributions: {1}" )]
[Alias('Name')]
[string]$WslInstanceName,
[Parameter(Mandatory)][Alias('Gateway')]
[ipaddress]$GatewayIpAddress,
[Alias('Prefix')]
[int]$PrefixLength = 24,
[Alias('IpAddress')]
[ipaddress]$WslInstanceIpAddress,
[Parameter(Mandatory, ParameterSetName = 'SaveExternally')]
[ref]$Modified,
[Parameter(ParameterSetName = 'SaveHere')]
[switch]$BackupWslConfig
)
if ($PSCmdlet.ParameterSetName -eq 'SaveHere') {
$localModified = $false
$Modified = [ref]$localModified
}
Read-WslConfig -ConfigType 'WslIpHandler' | Out-Null
if ($null -eq $WslInstanceIpAddress) {
$existingIp = Get-WslConfigStaticIpAddress -Name $WslInstanceName
if ($existingIp) {
Write-Debug "$(_@) `$WslInstanceIpAddress is `$null. Using existing assignment:"
Write-Debug "$(_@) $WslInstanceName = $existingIp"
$WslInstanceIpAddress = $existingIp
}
else {
$WslInstanceIpAddress = Get-WslConfigAvailableStaticIpAddress $GatewayIpAddress
Write-Debug "$(_@) `$WslInstanceIpAddress is `$null."
Write-Debug "$(_@) Available Static Ip Address: $WslInstanceIpAddress"
}
}
Write-Debug "$(_@) `$WslInstanceName=$WslInstanceName `$GatewayIpAddress=$GatewayIpAddress `$PrefixLength=$PrefixLength `$WslInstanceIpAddress=$($WslInstanceIpAddress ? $WslInstanceIpAddress : "`$null")"
$null = Test-IsValidStaticIpAddress -IpAddress $WslInstanceIpAddress -GatewayIpAddress $GatewayIpAddress -PrefixLength $PrefixLength
Set-WslConfigStaticIpAddress -WslInstanceName $WslInstanceName -WslInstanceIpAddress $WslInstanceIpAddress -Modified $Modified
if ($PSCmdlet.ParameterSetName -eq 'SaveHere' -and $localModified) {
Write-WslConfig WslIpHandler -Backup:$BackupWslConfig
}
}
function Remove-WslInstanceStaticIpAddress {
<#
.SYNOPSIS
Removes Static IP Address for the specified WSL Instance from config file.
.DESCRIPTION
Removes Static IP Address for the specified WSL Instance from config file.
.PARAMETER WslInstanceName
Required. Name of WSL Instance as listed by `wsl.exe -l` command.
.PARAMETER Modified
Optional. Reference to boolean variable. Will be set to True if given parameters will lead to change of existing settings. If this parameter is specified - any occuring changes will have to be saved with Write-WslConfig command. This parameter cannot be used together with BackupWslConfig parameter.
.PARAMETER BackupWslConfig
Optional. If given - original version of config file will be saved as backup. This parameter cannot be used together with Modified parameter.
.EXAMPLE
Remove-WslInstanceStaticIpAddress -WslInstanceName Ubuntu
Will remove Static IP address for Ubuntu WSL Instance.
#>
param (
[Parameter(Mandatory)]
[ValidateSet([WslNamesGenerator],
ErrorMessage = "'{0}' is not one of the installed distributions: {1}" )]
[Alias('Name')]
[string]$WslInstanceName,
[Parameter(Mandatory, ParameterSetName = 'SaveExternally')]
[ref]$Modified,
[Parameter(ParameterSetName = 'SaveHere')]
[switch]$BackupWslConfig
)
Write-Debug "$(_@) `$PSBoundParameters: $(& {$args} @PSBoundParameters)"
if ($PSCmdlet.ParameterSetName -eq 'SaveHere') {
$localModified = $false
$Modified = [ref]$localModified
}
Read-WslConfig -ConfigType 'WslIpHandler' | Out-Null
Write-Debug "$(_@) Before Calling Remove-WslConfigValue `$Modified=$($Modified.Value)"
Remove-WslConfigStaticIpAddress -WslInstanceName $WslInstanceName -Modified $Modified
Write-Debug "$(_@) After Calling Remove-WslConfigValue `$Modified=$($Modified.Value)"
if ($PSCmdlet.ParameterSetName -eq 'SaveHere' -and $localModified) {
Write-Debug "$(_@) Calling Write-WslConfig -Backup:$BackupWslConfig"
Write-WslConfig WslIpHandler -Backup:$BackupWslConfig
}
}
function Set-WslNetworkConfig {
<#
.SYNOPSIS
Sets WSL Network Adapter parameters, which are stored in config file
.DESCRIPTION
Sets WSL Network Adapter parameters, which are stored in config file