Initial public release: netswitch v1.0.0

This commit is contained in:
engelgardt 2026-05-16 11:36:45 +03:00
commit cce5204c25
4 changed files with 224 additions and 0 deletions

12
.gitignore vendored Normal file
View file

@ -0,0 +1,12 @@
# Build output / staging
*.exe
portable-v*/
# Local backup of release archives
releases/
# Editor / OS junk
.vscode/
.idea/
.DS_Store
Thumbs.db

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 engelgardt
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

48
README.md Normal file
View file

@ -0,0 +1,48 @@
# netswitch
[![Latest release](https://img.shields.io/github/v/release/Engelgardt23/netswitch)](https://github.com/Engelgardt23/netswitch/releases/latest)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
A tiny portable tool to flip a Windows network adapter between a **static IP** and **DHCP** with a few keystrokes.
Built for the recurring engineer chore of "give my laptop NIC 10.10.10.1 so I can talk to a server's BMC" and "now put it back on DHCP so I can have internet again."
> **Made by engelgardt.**
---
## Download
Grab the latest release: [**releases page**](https://github.com/Engelgardt23/netswitch/releases/latest).
The asset is `netswitch-portable-vX.Y.Z.zip` (~30 KB).
## Run
1. Unzip anywhere.
2. Double-click `netswitch.exe`.
3. Accept the UAC prompt (admin is needed for `netsh interface ipv4 set address`).
4. Pick the network adapter from the list.
5. Choose mode:
- **Static**: enter IP (default `10.10.10.1`), mask (default `255.255.255.0`), gateway (optional).
- **DHCP**: just confirms — the NIC reverts to DHCP for both IP and DNS.
## What it filters
Only real, wired physical adapters appear in the picker. Wireless, VPN, virtual, Hyper-V, VMware, VirtualBox, TAP/TUN, WireGuard, OpenVPN, Tailscale, ZeroTier, Bluetooth, Loopback, WAN Miniport — all skipped.
## Update check
On every launch the tool calls GitHub's `/releases/latest` (3-second timeout). If a newer version is available, it prints a yellow notice and offers to open the download page in your browser. If you're offline, it stays silent.
## Build from source
The script is a single `netswitch.ps1`. To rebuild the bundled `.exe`:
```
Install-Module ps2exe -Scope CurrentUser
Invoke-ps2exe -inputFile netswitch.ps1 -outputFile netswitch.exe -requireAdmin -title "netswitch" -version 1.0.0.0
```
## License
MIT — see [LICENSE](LICENSE).

143
netswitch.ps1 Normal file
View file

@ -0,0 +1,143 @@
# netswitch v1.0.0 - quick NIC IP / DHCP toggle
# made by engelgardt
$NetswitchVersion = '1.0.0'
$GithubRepo = 'Engelgardt23/netswitch'
$ErrorActionPreference = 'Stop'
# --- Self-elevate if not admin (no-op when launched from the ps2exe build which already requests admin) ---
$me = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()
if (-not $me.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
if ($PSCommandPath) {
Start-Process -FilePath 'powershell.exe' `
-ArgumentList @('-NoProfile','-ExecutionPolicy','Bypass','-File',"`"$PSCommandPath`"") `
-Verb RunAs
} else {
Start-Process -FilePath ([System.Diagnostics.Process]::GetCurrentProcess().MainModule.FileName) -Verb RunAs
}
exit
}
# --- Banner ---
Write-Host ""
Write-Host "==============================================" -ForegroundColor Cyan
Write-Host " netswitch v$NetswitchVersion - NIC IP/DHCP toggle" -ForegroundColor Cyan
Write-Host " made by engelgardt" -ForegroundColor DarkCyan
Write-Host "==============================================" -ForegroundColor Cyan
Write-Host ""
# --- Update check ---
function Test-NetswitchUpdate {
try {
$url = "https://api.github.com/repos/$GithubRepo/releases/latest"
$r = Invoke-RestMethod -Uri $url -TimeoutSec 3 -Headers @{ 'User-Agent' = "netswitch/$NetswitchVersion" }
$latest = ($r.tag_name -as [string]) -replace '^v',''
if (-not $latest) { return }
$toTuple = { param($s)
$parts = ($s -split '\.') | ForEach-Object {
$n = 0; [void][int]::TryParse($_, [ref]$n); $n
}
while ($parts.Count -lt 3) { $parts += 0 }
,$parts[0..2]
}
$L = & $toTuple $latest
$C = & $toTuple $NetswitchVersion
$isNewer = $false
for ($i = 0; $i -lt 3; $i++) {
if ($L[$i] -gt $C[$i]) { $isNewer = $true; break }
if ($L[$i] -lt $C[$i]) { break }
}
if ($isNewer) {
Write-Host "Update available: v$NetswitchVersion -> $($r.tag_name)" -ForegroundColor Yellow
$ans = Read-Host "Open the download page in your browser? [Y/n]"
if ($ans -notmatch '^(n|N|no|NO)$') {
Start-Process $r.html_url
}
Write-Host ""
}
} catch {
# silent on offline / API errors
}
}
Test-NetswitchUpdate
# --- Pick adapter (physical wired only) ---
$skipDescriptionPattern = 'VPN|Virtual|AnyConnect|TAP-|TUN-|Bluetooth|Loopback|WAN Miniport|Hyper-V|VMware|VirtualBox|WireGuard|OpenVPN|Tailscale|ZeroTier'
$skipMediaTypes = @('Native 802.11', 'Wireless WAN')
$adapters = @(Get-NetAdapter | Where-Object {
$_.Status -notin @('Disabled','Not Present') -and
-not $_.Virtual -and
$_.MediaType -notin $skipMediaTypes -and
$_.InterfaceDescription -notmatch $skipDescriptionPattern -and
$_.Name -notmatch $skipDescriptionPattern
} | Sort-Object ifIndex)
if ($adapters.Count -eq 0) {
Write-Host "No physical wired adapters found." -ForegroundColor Red
Read-Host "Press Enter to exit"; exit 1
}
Write-Host "Available adapters:"
for ($i = 0; $i -lt $adapters.Count; $i++) {
$a = $adapters[$i]
$ips = (Get-NetIPAddress -InterfaceIndex $a.ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue |
Where-Object { $_.PrefixOrigin -ne 'WellKnown' }).IPAddress -join ', '
Write-Host (" {0}) [{1,-12}] {2} ({3}) {4}" -f ($i + 1), $a.Status, $a.Name, $a.InterfaceDescription, $ips)
}
do {
$sel = (Read-Host "Select adapter number").Trim()
$valid = ($sel -match '^\d+$') -and ([int]$sel -ge 1) -and ([int]$sel -le $adapters.Count)
if (-not $valid) { Write-Host "Invalid selection." -ForegroundColor Red }
} while (-not $valid)
$nic = $adapters[[int]$sel - 1]
Write-Host ""
Write-Host "Selected: $($nic.Name)" -ForegroundColor Green
# --- Mode ---
Write-Host ""
Write-Host "Mode:"
Write-Host " 1) Static IP"
Write-Host " 2) DHCP"
$modeChoice = Read-Host "Choice [1]"
if ([string]::IsNullOrWhiteSpace($modeChoice)) { $modeChoice = '1' }
if ($modeChoice.Trim() -eq '2') {
# --- DHCP ---
Write-Host ""
Write-Host "Setting $($nic.Name) to DHCP..." -ForegroundColor Yellow
& netsh interface ipv4 set address name="$($nic.Name)" source=dhcp
& netsh interface ipv4 set dnsservers name="$($nic.Name)" source=dhcp
Write-Host "Done." -ForegroundColor Green
}
else {
# --- Static ---
$ip = Read-Host "IP address [10.10.10.1]"
if ([string]::IsNullOrWhiteSpace($ip)) { $ip = '10.10.10.1' }
$mask = Read-Host "Subnet mask [255.255.255.0]"
if ([string]::IsNullOrWhiteSpace($mask)) { $mask = '255.255.255.0' }
$gw = Read-Host "Gateway (Enter to skip)"
Write-Host ""
Write-Host "Setting $($nic.Name) -> $ip / $mask$( if ($gw) { " via $gw" })" -ForegroundColor Yellow
if ([string]::IsNullOrWhiteSpace($gw)) {
& netsh interface ipv4 set address name="$($nic.Name)" static $ip $mask
} else {
& netsh interface ipv4 set address name="$($nic.Name)" static $ip $mask $gw
}
Write-Host "Done." -ForegroundColor Green
}
# --- Show current state ---
Write-Host ""
Write-Host "Current IPv4 config:" -ForegroundColor Cyan
Get-NetIPAddress -InterfaceIndex $nic.ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue |
Where-Object { $_.PrefixOrigin -ne 'WellKnown' } |
Format-Table IPAddress, PrefixLength, PrefixOrigin -AutoSize
Read-Host "Press Enter to exit"