diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..77edc79 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# Ignore local network presets +network-presets.json diff --git a/NetworkAdapter.psm1 b/NetworkAdapter.psm1 new file mode 100644 index 0000000..01d3397 --- /dev/null +++ b/NetworkAdapter.psm1 @@ -0,0 +1,778 @@ +function Show-Header { + Clear-Host + Write-Host "============================================" -ForegroundColor Cyan + Write-Host " Network Adapter Configuration Tool" -ForegroundColor Cyan + Write-Host "============================================" -ForegroundColor Cyan + Write-Host "" +} + +function Get-AdapterChoice { + Show-Header + Write-Host "Available Network Adapters:" -ForegroundColor Yellow + Write-Host "" + + while ($true) { + $adapters = Get-NetAdapter | Where-Object { $_.Status -ne 'Disabled' } + + if ($adapters.Count -eq 0) { + Write-Host "No active network adapters found!" -ForegroundColor Red + Read-Host "Press Enter to exit" + exit + } + + $index = 1 + foreach ($adapter in $adapters) { + $status = if ($adapter.Status -eq 'Up') { + "[CONNECTED]" + } else { + "[" + $adapter.Status.ToUpper() + "]" + } + + # Get IP address + $ipv4 = Get-NetIPAddress -InterfaceIndex $adapter.InterfaceIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue + $ipAddress = if ($ipv4) { $ipv4.IPAddress } else { "No IP" } + + # Get MAC address in both formats + $macWindows = $adapter.MacAddress + $macLinux = $macWindows -replace '-', ':' + + Write-Host "$index. $($adapter.Name)" -ForegroundColor White + + # Colored description + status + $statusLabel = if ($adapter.Status -eq 'Up') { 'CONNECTED' } else { $adapter.Status.ToUpper() } + if ($adapter.Status -eq 'Up') { $statusColor = 'Green' } + elseif ($adapter.Status -in @('Down','NotPresent','Not Present')) { $statusColor = 'Red' } + else { $statusColor = 'Yellow' } + + Write-Host " $($adapter.InterfaceDescription) " -ForegroundColor Gray -NoNewline + Write-Host "[$statusLabel]" -ForegroundColor $statusColor + + # Colored IP and MAC display + if ($ipv4) { + Write-Host " IP: $ipAddress" -ForegroundColor Cyan -NoNewline + } else { + Write-Host " IP: No IP" -ForegroundColor DarkYellow -NoNewline + } + Write-Host " | MAC: $macWindows ($macLinux)" -ForegroundColor DarkGray + $index++ + } + + Write-Host "" + Write-Host "R. Refresh list" -ForegroundColor White + Write-Host "P. Add/Remove from System PATH" -ForegroundColor White + Write-Host "0. Exit" -ForegroundColor Gray + Write-Host "" + + do { + $choice = Read-Host "Select adapter number" + if ($choice -eq '0') { + exit + } + if ($choice -in @('P','p')) { + Add-ToSystemPath + return $null + } + if ($choice -in @('R','r')) { + break + } + + if ($choice -match '^[0-9]+$') { + $choiceNum = [int]$choice + } else { + $choiceNum = -1 + } + } while ($choiceNum -lt 1 -or $choiceNum -gt $adapters.Count) + + if ($choice -in @('R','r')) { + Clear-Host + Show-Header + continue + } + + return $adapters[$choiceNum - 1] + } +} + +function Show-AdapterInfo { + param ( + [Parameter(Mandatory=$true)] + $Adapter + ) + + Show-Header + Write-Host "Adapter: $($Adapter.Name)" -ForegroundColor Green + Write-Host "Description: $($Adapter.InterfaceDescription)" -ForegroundColor White + Write-Host "Status: $($Adapter.Status)" -ForegroundColor $(if ($Adapter.Status -eq 'Up') { 'Green' } else { 'Yellow' }) + Write-Host "" + + Write-Host "Hardware Information:" -ForegroundColor Yellow + $macWindows = $Adapter.MacAddress + $macLinux = $macWindows -replace '-', ':' + Write-Host " MAC Address: $macWindows (Windows)" -ForegroundColor White + Write-Host " MAC Address: $macLinux (Linux/Unix)" -ForegroundColor White + Write-Host " Link Speed: $($Adapter.LinkSpeed)" -ForegroundColor White + Write-Host "" + + $ipConfig = Get-NetIPConfiguration -InterfaceIndex $Adapter.InterfaceIndex -ErrorAction SilentlyContinue + $ipv4 = Get-NetIPAddress -InterfaceIndex $Adapter.InterfaceIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue + + Write-Host "IPv4 Configuration:" -ForegroundColor Yellow + + if ($ipv4) { + Write-Host " IP Address: $($ipv4.IPAddress)" -ForegroundColor White + Write-Host " Subnet Prefix Length: $($ipv4.PrefixLength) ($(Convert-PrefixToSubnetMask $ipv4.PrefixLength))" -ForegroundColor White + Write-Host " DHCP Enabled: $($ipv4.PrefixOrigin -eq 'Dhcp')" -ForegroundColor White + } else { + Write-Host " IP Address: Not configured" -ForegroundColor Gray + } + + if ($ipConfig.IPv4DefaultGateway) { + Write-Host " Default Gateway: $($ipConfig.IPv4DefaultGateway.NextHop)" -ForegroundColor White + } else { + Write-Host " Default Gateway: Not configured" -ForegroundColor Gray + } + + $dnsServers = Get-DnsClientServerAddress -InterfaceIndex $Adapter.InterfaceIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue + if ($dnsServers -and $dnsServers.ServerAddresses) { + Write-Host " DNS Servers:" -ForegroundColor White + foreach ($dns in $dnsServers.ServerAddresses) { + Write-Host " - $dns" -ForegroundColor White + } + } else { + Write-Host " DNS Servers: Not configured" -ForegroundColor Gray + } + + Write-Host "" + + # Adapter details menu: include DHCP, manual IP, lease refresh, DNS flush and presets + while ($true) { + Write-Host "Options:" -ForegroundColor Yellow + Write-Host "1. Set DHCP (enable DHCP on adapter)" -ForegroundColor White + Write-Host "2. Set Manual IP" -ForegroundColor White + Write-Host "3. Refresh DHCP lease (release/renew)" -ForegroundColor White + Write-Host "4. Flush DNS cache" -ForegroundColor White + Write-Host "5. Presets" -ForegroundColor White + Write-Host "0. Back" -ForegroundColor Gray + Write-Host "" + + $sel = Read-Host "Select option" + switch ($sel) { + '0' { break } + '1' { + Set-DHCPConfiguration -Adapter $Adapter + # refresh adapter object + $Adapter = Get-NetAdapter -InterfaceIndex $Adapter.InterfaceIndex -ErrorAction SilentlyContinue + Clear-Host + Show-Header + Write-Host "Adapter: $($Adapter.Name)" -ForegroundColor Green + continue + } + '2' { + Set-ManualConfiguration -Adapter $Adapter + $Adapter = Get-NetAdapter -InterfaceIndex $Adapter.InterfaceIndex -ErrorAction SilentlyContinue + Clear-Host + Show-Header + Write-Host "Adapter: $($Adapter.Name)" -ForegroundColor Green + continue + } + '3' { + Refresh-DHCPLease -Adapter $Adapter + $Adapter = Get-NetAdapter -InterfaceIndex $Adapter.InterfaceIndex -ErrorAction SilentlyContinue + Clear-Host + Show-Header + Write-Host "Adapter: $($Adapter.Name)" -ForegroundColor Green + continue + } + '4' { + Flush-DNS + Clear-Host + Show-Header + Write-Host "Adapter: $($Adapter.Name)" -ForegroundColor Green + continue + } + '5' { + Show-PresetsMenu -Adapter $Adapter + $Adapter = Get-NetAdapter -InterfaceIndex $Adapter.InterfaceIndex -ErrorAction SilentlyContinue + Clear-Host + Show-Header + Write-Host "Adapter: $($Adapter.Name)" -ForegroundColor Green + continue + } + default { + Write-Host "Invalid selection" -ForegroundColor Red + Start-Sleep -Seconds 1 + Clear-Host + Show-Header + Write-Host "Adapter: $($Adapter.Name)" -ForegroundColor Green + continue + } + } + } +} + +function Convert-PrefixToSubnetMask { + param([int]$PrefixLength) + + $mask = ([Math]::Pow(2, 32) - [Math]::Pow(2, (32 - $PrefixLength))) + $bytes = [BitConverter]::GetBytes([UInt32]$mask) + [Array]::Reverse($bytes) + return ($bytes -join '.') +} + +function Convert-SubnetMaskToPrefix { + param([string]$SubnetMask) + + $octets = $SubnetMask -split '\.' + $binaryString = '' + foreach ($octet in $octets) { + $binaryString += [Convert]::ToString([int]$octet, 2).PadLeft(8, '0') + } + return ($binaryString.ToCharArray() | Where-Object { $_ -eq '1' }).Count +} + +function Add-ToSystemPath { + Write-Host "" + Write-Host "Add NetworkConfig to System PATH" -ForegroundColor Yellow + Write-Host "="*50 -ForegroundColor Yellow + Write-Host "" + + $scriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path + + # Check current PATH + $userPath = [Environment]::GetEnvironmentVariable('Path', 'User') + $machinePath = [Environment]::GetEnvironmentVariable('Path', 'Machine') + + $isInUserPath = $userPath -split ';' | Where-Object { $_ -eq $scriptPath } + $isInMachinePath = $machinePath -split ';' | Where-Object { $_ -eq $scriptPath } + + Write-Host "Script Location: $scriptPath" -ForegroundColor Cyan + Write-Host "" + + if ($isInUserPath) { + Write-Host "✓ Already in User PATH" -ForegroundColor Green + } + if ($isInMachinePath) { + Write-Host "✓ Already in System PATH" -ForegroundColor Green + } + + if ($isInUserPath -or $isInMachinePath) { + Write-Host "" + Write-Host "Options:" -ForegroundColor Yellow + Write-Host "1. Remove from User PATH" -ForegroundColor White + Write-Host "2. Remove from System PATH" -ForegroundColor White + Write-Host "0. Cancel" -ForegroundColor Gray + Write-Host "" + + $choice = Read-Host "Select option" + + switch ($choice) { + '1' { + if ($isInUserPath) { + try { + $newPath = ($userPath -split ';' | Where-Object { $_ -ne $scriptPath }) -join ';' + [Environment]::SetEnvironmentVariable('Path', $newPath, 'User') + Write-Host "Successfully removed from User PATH!" -ForegroundColor Green + Write-Host "You may need to restart your terminals for changes to take effect." -ForegroundColor Yellow + } + catch { + Write-Host "Error removing from User PATH: $($_.Exception.Message)" -ForegroundColor Red + } + } else { + Write-Host "Not found in User PATH." -ForegroundColor Yellow + } + } + '2' { + if ($isInMachinePath) { + try { + $newPath = ($machinePath -split ';' | Where-Object { $_ -ne $scriptPath }) -join ';' + [Environment]::SetEnvironmentVariable('Path', $newPath, 'Machine') + Write-Host "Successfully removed from System PATH!" -ForegroundColor Green + Write-Host "You may need to restart your terminals for changes to take effect." -ForegroundColor Yellow + } + catch { + Write-Host "Error removing from System PATH: $($_.Exception.Message)" -ForegroundColor Red + } + } else { + Write-Host "Not found in System PATH." -ForegroundColor Yellow + } + } + } + } else { + Write-Host "Not currently in PATH." -ForegroundColor Gray + Write-Host "" + Write-Host "Where would you like to add it?" -ForegroundColor Yellow + Write-Host "1. User PATH (current user only)" -ForegroundColor White + Write-Host "2. System PATH (all users - requires admin)" -ForegroundColor White + Write-Host "0. Cancel" -ForegroundColor Gray + Write-Host "" + + $choice = Read-Host "Select option" + + switch ($choice) { + '1' { + try { + $newPath = $userPath.TrimEnd(';') + ';' + $scriptPath + [Environment]::SetEnvironmentVariable('Path', $newPath, 'User') + Write-Host "Successfully added to User PATH!" -ForegroundColor Green + Write-Host "You can now run 'NetworkConfig' from any terminal." -ForegroundColor Green + Write-Host "You may need to restart your terminals for changes to take effect." -ForegroundColor Yellow + } + catch { + Write-Host "Error adding to User PATH: $($_.Exception.Message)" -ForegroundColor Red + } + } + '2' { + try { + $newPath = $machinePath.TrimEnd(';') + ';' + $scriptPath + [Environment]::SetEnvironmentVariable('Path', $newPath, 'Machine') + Write-Host "Successfully added to System PATH!" -ForegroundColor Green + Write-Host "You can now run 'NetworkConfig' from any terminal." -ForegroundColor Green + Write-Host "You may need to restart your terminals for changes to take effect." -ForegroundColor Yellow + } + catch { + Write-Host "Error adding to System PATH: $($_.Exception.Message)" -ForegroundColor Red + Write-Host "Make sure you're running as administrator." -ForegroundColor Yellow + } + } + } + } + + Write-Host "" + Read-Host "Press Enter to continue" +} + +function Refresh-DHCPLease { + param ( + [Parameter(Mandatory=$false)] + $Adapter + ) + + Write-Host "" + Write-Host "Refreshing DHCP lease..." -ForegroundColor Yellow + try { + # Attempt a client-side release/renew. This may act on all adapters on some systems. + Start-Process -FilePath ipconfig -ArgumentList '/release' -NoNewWindow -Wait + Start-Process -FilePath ipconfig -ArgumentList '/renew' -NoNewWindow -Wait + Write-Host "DHCP lease refreshed (client-side)." -ForegroundColor Green + } + catch { + Write-Host "Error refreshing DHCP lease: $($_.Exception.Message)" -ForegroundColor Red + } + + Write-Host "" + Read-Host "Press Enter to continue" +} + +function Flush-DNS { + param () + + Write-Host "" + Write-Host "Flushing DNS cache..." -ForegroundColor Yellow + try { + # Use native cmdlet when available + if (Get-Command -Name Clear-DnsClientCache -ErrorAction SilentlyContinue) { + Clear-DnsClientCache -ErrorAction Stop + } + + # Also run ipconfig flushdns for broad compatibility + Start-Process -FilePath ipconfig -ArgumentList '/flushdns' -NoNewWindow -Wait + + Write-Host "DNS cache flushed." -ForegroundColor Green + } + catch { + Write-Host "Error flushing DNS cache: $($_.Exception.Message)" -ForegroundColor Red + } + + Write-Host "" + Read-Host "Press Enter to continue" +} + +function Set-DHCPConfiguration { + param ( + [Parameter(Mandatory=$true)] + $Adapter + ) + + Write-Host "" + Write-Host "Setting adapter to DHCP..." -ForegroundColor Yellow + + try { + # Remove existing IP configuration + Remove-NetIPAddress -InterfaceIndex $Adapter.InterfaceIndex -Confirm:$false -ErrorAction SilentlyContinue + Remove-NetRoute -InterfaceIndex $Adapter.InterfaceIndex -Confirm:$false -ErrorAction SilentlyContinue + + # Set to DHCP + Set-NetIPInterface -InterfaceIndex $Adapter.InterfaceIndex -Dhcp Enabled -ErrorAction Stop + Set-DnsClientServerAddress -InterfaceIndex $Adapter.InterfaceIndex -ResetServerAddresses -ErrorAction Stop + + Write-Host "Successfully configured adapter for DHCP!" -ForegroundColor Green + Write-Host "The adapter will now obtain IP settings automatically." -ForegroundColor Green + } + catch { + Write-Host "Error configuring DHCP: $($_.Exception.Message)" -ForegroundColor Red + } + + Write-Host "" + Read-Host "Press Enter to continue" +} + +function Set-ManualConfiguration { + param ( + [Parameter(Mandatory=$true)] + $Adapter + ) + + Write-Host "" + Write-Host "Manual IP Configuration" -ForegroundColor Yellow + Write-Host "Leave fields blank to skip configuration for that setting" -ForegroundColor Gray + Write-Host "" + + # Get IP Address + do { + $ipAddress = Read-Host "Enter IP Address (e.g., 192.168.1.100)" + if ([string]::IsNullOrWhiteSpace($ipAddress)) { + Write-Host "IP Address is required for manual configuration!" -ForegroundColor Red + } + } while ([string]::IsNullOrWhiteSpace($ipAddress)) + + # Validate IP Address + try { + [System.Net.IPAddress]::Parse($ipAddress) | Out-Null + } + catch { + Write-Host "Invalid IP Address format!" -ForegroundColor Red + Read-Host "Press Enter to return to menu" + return + } + + # Get Subnet Mask + do { + $subnetMask = Read-Host "Enter Subnet Mask (e.g., 255.255.255.0) or prefix length (e.g., 24)" + if ([string]::IsNullOrWhiteSpace($subnetMask)) { + Write-Host "Subnet Mask is required for manual configuration!" -ForegroundColor Red + } + } while ([string]::IsNullOrWhiteSpace($subnetMask)) + + # Convert subnet mask to prefix length + if ($subnetMask -match '^\d{1,2}$') { + $prefixLength = [int]$subnetMask + } else { + try { + $prefixLength = Convert-SubnetMaskToPrefix $subnetMask + } + catch { + Write-Host "Invalid Subnet Mask format!" -ForegroundColor Red + Read-Host "Press Enter to return to menu" + return + } + } + + # Get Default Gateway (optional) + $gateway = Read-Host "Enter Default Gateway (press Enter to skip)" + if (![string]::IsNullOrWhiteSpace($gateway)) { + try { + [System.Net.IPAddress]::Parse($gateway) | Out-Null + } + catch { + Write-Host "Invalid Gateway format!" -ForegroundColor Red + Read-Host "Press Enter to return to menu" + return + } + } + + # Get DNS Servers (optional) + $dnsServers = @() + $dns1 = Read-Host "Enter Primary DNS Server (press Enter to skip)" + if (![string]::IsNullOrWhiteSpace($dns1)) { + try { + [System.Net.IPAddress]::Parse($dns1) | Out-Null + $dnsServers += $dns1 + } + catch { + Write-Host "Invalid DNS Server format!" -ForegroundColor Red + Read-Host "Press Enter to return to menu" + return + } + } + + $dns2 = Read-Host "Enter Secondary DNS Server (press Enter to skip)" + if (![string]::IsNullOrWhiteSpace($dns2)) { + try { + [System.Net.IPAddress]::Parse($dns2) | Out-Null + $dnsServers += $dns2 + } + catch { + Write-Host "Invalid DNS Server format!" -ForegroundColor Red + Read-Host "Press Enter to return to menu" + return + } + } + + # Confirm configuration + Write-Host "" + Write-Host "Configuration Summary:" -ForegroundColor Cyan + Write-Host " IP Address: $ipAddress/$prefixLength" -ForegroundColor White + if (![string]::IsNullOrWhiteSpace($gateway)) { + Write-Host " Gateway: $gateway" -ForegroundColor White + } + if ($dnsServers.Count -gt 0) { + Write-Host " DNS Servers: $($dnsServers -join ', ')" -ForegroundColor White + } + Write-Host "" + + $confirm = Read-Host "Apply this configuration? (Y/N)" + if ($confirm -ne 'Y' -and $confirm -ne 'y') { + Write-Host "Configuration cancelled." -ForegroundColor Yellow + Read-Host "Press Enter to continue" + return + } + + # Apply configuration + Write-Host "" + Write-Host "Applying configuration..." -ForegroundColor Yellow + + try { + # Remove existing IP configuration + Remove-NetIPAddress -InterfaceIndex $Adapter.InterfaceIndex -Confirm:$false -ErrorAction SilentlyContinue + Remove-NetRoute -InterfaceIndex $Adapter.InterfaceIndex -Confirm:$false -ErrorAction SilentlyContinue + + # Disable DHCP + Set-NetIPInterface -InterfaceIndex $Adapter.InterfaceIndex -Dhcp Disabled -ErrorAction Stop + + # Set IP Address + New-NetIPAddress -InterfaceIndex $Adapter.InterfaceIndex ` + -IPAddress $ipAddress ` + -PrefixLength $prefixLength ` + -DefaultGateway $(if (![string]::IsNullOrWhiteSpace($gateway)) { $gateway } else { $null }) ` + -ErrorAction Stop | Out-Null + + # Set DNS Servers + if ($dnsServers.Count -gt 0) { + Set-DnsClientServerAddress -InterfaceIndex $Adapter.InterfaceIndex ` + -ServerAddresses $dnsServers ` + -ErrorAction Stop + } + + Write-Host "Configuration applied successfully!" -ForegroundColor Green + } + catch { + Write-Host "Error applying configuration: $($_.Exception.Message)" -ForegroundColor Red + } + + Write-Host "" + Read-Host "Press Enter to continue" +} + +# Presets helpers +function Get-PresetsFilePath { + return Join-Path $PSScriptRoot 'network-presets.json' +} + +function Load-Presets { + $file = Get-PresetsFilePath + if (-not (Test-Path $file)) { + return @() + } + + try { + $raw = Get-Content -Path $file -Raw -ErrorAction Stop + if ([string]::IsNullOrWhiteSpace($raw)) { return @() } + return $raw | ConvertFrom-Json -ErrorAction Stop + } + catch { + Write-Host "Error loading presets file: $($_.Exception.Message)" -ForegroundColor Red + return @() + } +} + +function Save-Presets($data) { + $file = Get-PresetsFilePath + try { + $json = $data | ConvertTo-Json -Depth 10 + $json | Set-Content -Path $file -Encoding UTF8 -Force + return $true + } + catch { + Write-Host "Error saving presets file: $($_.Exception.Message)" -ForegroundColor Red + return $false + } +} + +function Get-PresetsForAdapter($adapterGuid) { + $all = Load-Presets + if ($null -eq $all) { return @() } + $entry = $all | Where-Object { $_.adapterGuid -eq $adapterGuid } + if ($entry) { return $entry.presets } + return @() +} + +function Add-OrUpdate-Preset($adapter, $preset) { + $all = Load-Presets + if (-not $all) { $all = @() } + + $entry = $all | Where-Object { $_.adapterGuid -eq $adapter.InterfaceGuid } + if (-not $entry) { + $entry = [PSCustomObject]@{ + adapterGuid = $adapter.InterfaceGuid + adapterName = $adapter.Name + adapterDescription = $adapter.InterfaceDescription + presets = @() + } + $all += $entry + } + + # Replace existing preset with same name + $existing = $entry.presets | Where-Object { $_.name -eq $preset.name } + if ($existing) { + $entry.presets = ($entry.presets | Where-Object { $_.name -ne $preset.name }) + $preset + } + else { + $entry.presets += $preset + } + + Save-Presets $all | Out-Null +} + +function Remove-Preset($adapterGuid, $presetName) { + $all = Load-Presets + if (-not $all) { return $false } + $entry = $all | Where-Object { $_.adapterGuid -eq $adapterGuid } + if (-not $entry) { return $false } + $entry.presets = $entry.presets | Where-Object { $_.name -ne $presetName } + Save-Presets $all | Out-Null + return $true +} + +function Apply-Preset($adapter, $preset) { + if ($preset.type -eq 'DHCP') { + Set-DHCPConfiguration -Adapter $adapter + return + } + + try { + Remove-NetIPAddress -InterfaceIndex $adapter.InterfaceIndex -Confirm:$false -ErrorAction SilentlyContinue + Remove-NetRoute -InterfaceIndex $adapter.InterfaceIndex -Confirm:$false -ErrorAction SilentlyContinue + + Set-NetIPInterface -InterfaceIndex $adapter.InterfaceIndex -Dhcp Disabled -ErrorAction Stop + + New-NetIPAddress -InterfaceIndex $adapter.InterfaceIndex -IPAddress $preset.ipAddress -PrefixLength $preset.prefixLength -DefaultGateway $(if ($preset.gateway) { $preset.gateway } else { $null }) -ErrorAction Stop | Out-Null + + if ($preset.dns -and $preset.dns.Count -gt 0) { + Set-DnsClientServerAddress -InterfaceIndex $adapter.InterfaceIndex -ServerAddresses $preset.dns -ErrorAction Stop + } + + Write-Host "Preset '$($preset.name)' applied successfully." -ForegroundColor Green + } + catch { + Write-Host "Error applying preset: $($_.Exception.Message)" -ForegroundColor Red + } + Read-Host "Press Enter to continue" +} + +function Show-PresetsMenu { + param([Parameter(Mandatory=$true)] $Adapter) + + while ($true) { + Clear-Host + Show-Header + Write-Host "Presets for adapter: $($Adapter.Name)" -ForegroundColor Yellow + $presets = Get-PresetsForAdapter $Adapter.InterfaceGuid + if ($presets.Count -eq 0) { + Write-Host " (No presets saved)" -ForegroundColor Gray + } + else { + $i = 1 + foreach ($p in $presets) { + Write-Host " $i. $($p.name) - $($p.type)" -ForegroundColor White + $i++ + } + } + + Write-Host ""; + Write-Host "A. Add current config as preset" -ForegroundColor White + Write-Host "L. Load/apply a preset" -ForegroundColor White + Write-Host "D. Delete a preset" -ForegroundColor White + Write-Host "E. Export presets file location" -ForegroundColor White + Write-Host "0. Back" -ForegroundColor Gray + Write-Host "" + + $sel = Read-Host "Select option" + switch ($sel) { + '0' { break } + 'A' { 'A' } 'a' { 'A' } default { } + } + + if ($sel -in @('A','a')) { + # Capture current IPv4 config + $ipConfig = Get-NetIPConfiguration -InterfaceIndex $Adapter.InterfaceIndex -ErrorAction SilentlyContinue + $ipv4 = Get-NetIPAddress -InterfaceIndex $Adapter.InterfaceIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue + + $presetName = Read-Host "Preset name" + if ([string]::IsNullOrWhiteSpace($presetName)) { Write-Host "Name required" -ForegroundColor Red; Start-Sleep -Seconds 1; continue } + + if ($ipv4) { + $presetObj = [PSCustomObject]@{ + name = $presetName + type = 'Manual' + ipAddress = $ipv4.IPAddress + prefixLength = $ipv4.PrefixLength + gateway = (if ($ipConfig.IPv4DefaultGateway) { $ipConfig.IPv4DefaultGateway.NextHop } else { $null }) + dns = (Get-DnsClientServerAddress -InterfaceIndex $Adapter.InterfaceIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue).ServerAddresses + } + } + else { + $presetObj = [PSCustomObject]@{ + name = $presetName + type = 'DHCP' + } + } + + Add-OrUpdate-Preset -adapter $Adapter -preset $presetObj + Write-Host "Preset saved." -ForegroundColor Green + Start-Sleep -Seconds 1 + continue + + } + + if ($sel -in @('L','l')) { + if ($presets.Count -eq 0) { Write-Host "No presets." -ForegroundColor Yellow; Start-Sleep -Seconds 1; continue } + $idx = Read-Host "Enter preset number to apply" + if ($idx -match '^[0-9]+$') { + $num = [int]$idx + if ($num -ge 1 -and $num -le $presets.Count) { + $preset = $presets[$num - 1] + Apply-Preset -Adapter $Adapter -preset $preset + } + else { Write-Host "Invalid selection" -ForegroundColor Red; Start-Sleep -Seconds 1 } + } + else { Write-Host "Invalid input" -ForegroundColor Red; Start-Sleep -Seconds 1 } + continue + } + + if ($sel -in @('D','d')) { + if ($presets.Count -eq 0) { Write-Host "No presets." -ForegroundColor Yellow; Start-Sleep -Seconds 1; continue } + $idx = Read-Host "Enter preset number to delete" + if ($idx -match '^[0-9]+$') { + $num = [int]$idx + if ($num -ge 1 -and $num -le $presets.Count) { + $name = $presets[$num - 1].name + Remove-Preset -adapterGuid $Adapter.InterfaceGuid -presetName $name | Out-Null + Write-Host "Deleted preset '$name'" -ForegroundColor Green + } + else { Write-Host "Invalid selection" -ForegroundColor Red } + } + else { Write-Host "Invalid input" -ForegroundColor Red } + Start-Sleep -Seconds 1 + continue + } + + if ($sel -in @('E','e')) { + $file = Get-PresetsFilePath + Write-Host "Presets file: $file" -ForegroundColor Cyan + Read-Host "Press Enter to continue" + continue + } + } +} + +Export-ModuleMember -Function Show-Header, Get-AdapterChoice, Show-AdapterInfo, Convert-PrefixToSubnetMask, Convert-SubnetMaskToPrefix, Add-ToSystemPath, Set-DHCPConfiguration, Set-ManualConfiguration, Get-PresetsFilePath, Load-Presets, Save-Presets, Get-PresetsForAdapter, Add-OrUpdate-Preset, Remove-Preset, Apply-Preset, Show-PresetsMenu, Refresh-DHCPLease, Flush-DNS diff --git a/Set-NetworkAdapter.ps1 b/Set-NetworkAdapter.ps1 index c37bcd4..2b4a98a 100644 --- a/Set-NetworkAdapter.ps1 +++ b/Set-NetworkAdapter.ps1 @@ -1,464 +1,14 @@ -#Requires -RunAsAdministrator +"#Requires -RunAsAdministrator" -<# -.SYNOPSIS - Network Adapter Configuration Script -.DESCRIPTION - Allows quick configuration of network adapter IP settings including DHCP and manual configuration -#> - -function Show-Header { - Clear-Host - Write-Host "============================================" -ForegroundColor Cyan - Write-Host " Network Adapter Configuration Tool" -ForegroundColor Cyan - Write-Host "============================================" -ForegroundColor Cyan - Write-Host "" -} - -function Get-AdapterChoice { - Show-Header - Write-Host "Available Network Adapters:" -ForegroundColor Yellow - Write-Host "" - - while ($true) { - $adapters = Get-NetAdapter | Where-Object { $_.Status -ne 'Disabled' } - - if ($adapters.Count -eq 0) { - Write-Host "No active network adapters found!" -ForegroundColor Red - Read-Host "Press Enter to exit" - exit - } - - $index = 1 - foreach ($adapter in $adapters) { - $status = if ($adapter.Status -eq 'Up') { - "[CONNECTED]" - } else { - "[" + $adapter.Status.ToUpper() + "]" - } - - # Get IP address - $ipv4 = Get-NetIPAddress -InterfaceIndex $adapter.InterfaceIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue - $ipAddress = if ($ipv4) { $ipv4.IPAddress } else { "No IP" } - - # Get MAC address in both formats - $macWindows = $adapter.MacAddress - $macLinux = $macWindows -replace '-', ':' - - Write-Host "$index. $($adapter.Name)" -ForegroundColor White - - # Colored description + status - $statusLabel = if ($adapter.Status -eq 'Up') { 'CONNECTED' } else { $adapter.Status.ToUpper() } - if ($adapter.Status -eq 'Up') { $statusColor = 'Green' } - elseif ($adapter.Status -in @('Down','NotPresent','Not Present')) { $statusColor = 'Red' } - else { $statusColor = 'Yellow' } - - Write-Host " $($adapter.InterfaceDescription) " -ForegroundColor Gray -NoNewline - Write-Host "[$statusLabel]" -ForegroundColor $statusColor - - # Colored IP and MAC display - if ($ipv4) { - Write-Host " IP: $ipAddress" -ForegroundColor Cyan -NoNewline - } else { - Write-Host " IP: No IP" -ForegroundColor DarkYellow -NoNewline - } - Write-Host " | MAC: $macWindows ($macLinux)" -ForegroundColor DarkGray - $index++ - } - - Write-Host "" - Write-Host "R. Refresh list" -ForegroundColor White - Write-Host "P. Add/Remove from System PATH" -ForegroundColor White - Write-Host "0. Exit" -ForegroundColor Gray - Write-Host "" - - do { - $choice = Read-Host "Select adapter number" - if ($choice -eq '0') { - exit - } - if ($choice -in @('P','p')) { - Add-ToSystemPath - return $null - } - if ($choice -in @('R','r')) { - break - } - - if ($choice -match '^[0-9]+$') { - $choiceNum = [int]$choice - } else { - $choiceNum = -1 - } - } while ($choiceNum -lt 1 -or $choiceNum -gt $adapters.Count) - - if ($choice -in @('R','r')) { - Clear-Host - Show-Header - continue - } - - return $adapters[$choiceNum - 1] - } -} - -function Show-AdapterInfo { - param ( - [Parameter(Mandatory=$true)] - $Adapter - ) - - Show-Header - Write-Host "Adapter: $($Adapter.Name)" -ForegroundColor Green - Write-Host "Description: $($Adapter.InterfaceDescription)" -ForegroundColor White - Write-Host "Status: $($Adapter.Status)" -ForegroundColor $(if ($Adapter.Status -eq 'Up') { 'Green' } else { 'Yellow' }) - Write-Host "" - - Write-Host "Hardware Information:" -ForegroundColor Yellow - $macWindows = $Adapter.MacAddress - $macLinux = $macWindows -replace '-', ':' - Write-Host " MAC Address: $macWindows (Windows)" -ForegroundColor White - Write-Host " MAC Address: $macLinux (Linux/Unix)" -ForegroundColor White - Write-Host " Link Speed: $($Adapter.LinkSpeed)" -ForegroundColor White - Write-Host "" - - $ipConfig = Get-NetIPConfiguration -InterfaceIndex $Adapter.InterfaceIndex -ErrorAction SilentlyContinue - $ipv4 = Get-NetIPAddress -InterfaceIndex $Adapter.InterfaceIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue - - Write-Host "IPv4 Configuration:" -ForegroundColor Yellow - - if ($ipv4) { - Write-Host " IP Address: $($ipv4.IPAddress)" -ForegroundColor White - Write-Host " Subnet Prefix Length: $($ipv4.PrefixLength) ($(Convert-PrefixToSubnetMask $ipv4.PrefixLength))" -ForegroundColor White - Write-Host " DHCP Enabled: $($ipv4.PrefixOrigin -eq 'Dhcp')" -ForegroundColor White - } else { - Write-Host " IP Address: Not configured" -ForegroundColor Gray - } - - if ($ipConfig.IPv4DefaultGateway) { - Write-Host " Default Gateway: $($ipConfig.IPv4DefaultGateway.NextHop)" -ForegroundColor White - } else { - Write-Host " Default Gateway: Not configured" -ForegroundColor Gray - } - - $dnsServers = Get-DnsClientServerAddress -InterfaceIndex $Adapter.InterfaceIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue - if ($dnsServers -and $dnsServers.ServerAddresses) { - Write-Host " DNS Servers:" -ForegroundColor White - foreach ($dns in $dnsServers.ServerAddresses) { - Write-Host " - $dns" -ForegroundColor White - } - } else { - Write-Host " DNS Servers: Not configured" -ForegroundColor Gray - } - - Write-Host "" -} - -function Convert-PrefixToSubnetMask { - param([int]$PrefixLength) - - $mask = ([Math]::Pow(2, 32) - [Math]::Pow(2, (32 - $PrefixLength))) - $bytes = [BitConverter]::GetBytes([UInt32]$mask) - [Array]::Reverse($bytes) - return ($bytes -join '.') -} - -function Convert-SubnetMaskToPrefix { - param([string]$SubnetMask) - - $octets = $SubnetMask -split '\.' - $binaryString = '' - foreach ($octet in $octets) { - $binaryString += [Convert]::ToString([int]$octet, 2).PadLeft(8, '0') - } - return ($binaryString.ToCharArray() | Where-Object { $_ -eq '1' }).Count -} - -function Add-ToSystemPath { - Write-Host "" - Write-Host "Add NetworkConfig to System PATH" -ForegroundColor Yellow - Write-Host "="*50 -ForegroundColor Yellow - Write-Host "" - - $scriptPath = Split-Path -Parent $PSCommandPath - - # Check current PATH - $userPath = [Environment]::GetEnvironmentVariable('Path', 'User') - $machinePath = [Environment]::GetEnvironmentVariable('Path', 'Machine') - - $isInUserPath = $userPath -split ';' | Where-Object { $_ -eq $scriptPath } - $isInMachinePath = $machinePath -split ';' | Where-Object { $_ -eq $scriptPath } - - Write-Host "Script Location: $scriptPath" -ForegroundColor Cyan - Write-Host "" - - if ($isInUserPath) { - Write-Host "✓ Already in User PATH" -ForegroundColor Green - } - if ($isInMachinePath) { - Write-Host "✓ Already in System PATH" -ForegroundColor Green - } - - if ($isInUserPath -or $isInMachinePath) { - Write-Host "" - Write-Host "Options:" -ForegroundColor Yellow - Write-Host "1. Remove from User PATH" -ForegroundColor White - Write-Host "2. Remove from System PATH" -ForegroundColor White - Write-Host "0. Cancel" -ForegroundColor Gray - Write-Host "" - - $choice = Read-Host "Select option" - - switch ($choice) { - '1' { - if ($isInUserPath) { - try { - $newPath = ($userPath -split ';' | Where-Object { $_ -ne $scriptPath }) -join ';' - [Environment]::SetEnvironmentVariable('Path', $newPath, 'User') - Write-Host "Successfully removed from User PATH!" -ForegroundColor Green - Write-Host "You may need to restart your terminals for changes to take effect." -ForegroundColor Yellow - } - catch { - Write-Host "Error removing from User PATH: $($_.Exception.Message)" -ForegroundColor Red - } - } else { - Write-Host "Not found in User PATH." -ForegroundColor Yellow - } - } - '2' { - if ($isInMachinePath) { - try { - $newPath = ($machinePath -split ';' | Where-Object { $_ -ne $scriptPath }) -join ';' - [Environment]::SetEnvironmentVariable('Path', $newPath, 'Machine') - Write-Host "Successfully removed from System PATH!" -ForegroundColor Green - Write-Host "You may need to restart your terminals for changes to take effect." -ForegroundColor Yellow - } - catch { - Write-Host "Error removing from System PATH: $($_.Exception.Message)" -ForegroundColor Red - } - } else { - Write-Host "Not found in System PATH." -ForegroundColor Yellow - } - } - } - } else { - Write-Host "Not currently in PATH." -ForegroundColor Gray - Write-Host "" - Write-Host "Where would you like to add it?" -ForegroundColor Yellow - Write-Host "1. User PATH (current user only)" -ForegroundColor White - Write-Host "2. System PATH (all users - requires admin)" -ForegroundColor White - Write-Host "0. Cancel" -ForegroundColor Gray - Write-Host "" - - $choice = Read-Host "Select option" - - switch ($choice) { - '1' { - try { - $newPath = $userPath.TrimEnd(';') + ';' + $scriptPath - [Environment]::SetEnvironmentVariable('Path', $newPath, 'User') - Write-Host "Successfully added to User PATH!" -ForegroundColor Green - Write-Host "You can now run 'NetworkConfig' from any terminal." -ForegroundColor Green - Write-Host "You may need to restart your terminals for changes to take effect." -ForegroundColor Yellow - } - catch { - Write-Host "Error adding to User PATH: $($_.Exception.Message)" -ForegroundColor Red - } - } - '2' { - try { - $newPath = $machinePath.TrimEnd(';') + ';' + $scriptPath - [Environment]::SetEnvironmentVariable('Path', $newPath, 'Machine') - Write-Host "Successfully added to System PATH!" -ForegroundColor Green - Write-Host "You can now run 'NetworkConfig' from any terminal." -ForegroundColor Green - Write-Host "You may need to restart your terminals for changes to take effect." -ForegroundColor Yellow - } - catch { - Write-Host "Error adding to System PATH: $($_.Exception.Message)" -ForegroundColor Red - Write-Host "Make sure you're running as administrator." -ForegroundColor Yellow - } - } - } - } - - Write-Host "" - Read-Host "Press Enter to continue" -} - -function Set-DHCPConfiguration { - param ( - [Parameter(Mandatory=$true)] - $Adapter - ) - - Write-Host "" - Write-Host "Setting adapter to DHCP..." -ForegroundColor Yellow - - try { - # Remove existing IP configuration - Remove-NetIPAddress -InterfaceIndex $Adapter.InterfaceIndex -Confirm:$false -ErrorAction SilentlyContinue - Remove-NetRoute -InterfaceIndex $Adapter.InterfaceIndex -Confirm:$false -ErrorAction SilentlyContinue - - # Set to DHCP - Set-NetIPInterface -InterfaceIndex $Adapter.InterfaceIndex -Dhcp Enabled -ErrorAction Stop - Set-DnsClientServerAddress -InterfaceIndex $Adapter.InterfaceIndex -ResetServerAddresses -ErrorAction Stop - - Write-Host "Successfully configured adapter for DHCP!" -ForegroundColor Green - Write-Host "The adapter will now obtain IP settings automatically." -ForegroundColor Green - } - catch { - Write-Host "Error configuring DHCP: $($_.Exception.Message)" -ForegroundColor Red - } - - Write-Host "" - Read-Host "Press Enter to continue" -} - -function Set-ManualConfiguration { - param ( - [Parameter(Mandatory=$true)] - $Adapter - ) - - Write-Host "" - Write-Host "Manual IP Configuration" -ForegroundColor Yellow - Write-Host "Leave fields blank to skip configuration for that setting" -ForegroundColor Gray - Write-Host "" - - # Get IP Address - do { - $ipAddress = Read-Host "Enter IP Address (e.g., 192.168.1.100)" - if ([string]::IsNullOrWhiteSpace($ipAddress)) { - Write-Host "IP Address is required for manual configuration!" -ForegroundColor Red - } - } while ([string]::IsNullOrWhiteSpace($ipAddress)) - - # Validate IP Address - try { - [System.Net.IPAddress]::Parse($ipAddress) | Out-Null - } - catch { - Write-Host "Invalid IP Address format!" -ForegroundColor Red - Read-Host "Press Enter to return to menu" - return - } - - # Get Subnet Mask - do { - $subnetMask = Read-Host "Enter Subnet Mask (e.g., 255.255.255.0) or prefix length (e.g., 24)" - if ([string]::IsNullOrWhiteSpace($subnetMask)) { - Write-Host "Subnet Mask is required for manual configuration!" -ForegroundColor Red - } - } while ([string]::IsNullOrWhiteSpace($subnetMask)) - - # Convert subnet mask to prefix length - if ($subnetMask -match '^\d{1,2}$') { - $prefixLength = [int]$subnetMask - } else { - try { - $prefixLength = Convert-SubnetMaskToPrefix $subnetMask - } - catch { - Write-Host "Invalid Subnet Mask format!" -ForegroundColor Red - Read-Host "Press Enter to return to menu" - return - } - } - - # Get Default Gateway (optional) - $gateway = Read-Host "Enter Default Gateway (press Enter to skip)" - if (![string]::IsNullOrWhiteSpace($gateway)) { - try { - [System.Net.IPAddress]::Parse($gateway) | Out-Null - } - catch { - Write-Host "Invalid Gateway format!" -ForegroundColor Red - Read-Host "Press Enter to return to menu" - return - } - } - - # Get DNS Servers (optional) - $dnsServers = @() - $dns1 = Read-Host "Enter Primary DNS Server (press Enter to skip)" - if (![string]::IsNullOrWhiteSpace($dns1)) { - try { - [System.Net.IPAddress]::Parse($dns1) | Out-Null - $dnsServers += $dns1 - } - catch { - Write-Host "Invalid DNS Server format!" -ForegroundColor Red - Read-Host "Press Enter to return to menu" - return - } - } - - $dns2 = Read-Host "Enter Secondary DNS Server (press Enter to skip)" - if (![string]::IsNullOrWhiteSpace($dns2)) { - try { - [System.Net.IPAddress]::Parse($dns2) | Out-Null - $dnsServers += $dns2 - } - catch { - Write-Host "Invalid DNS Server format!" -ForegroundColor Red - Read-Host "Press Enter to return to menu" - return - } - } - - # Confirm configuration - Write-Host "" - Write-Host "Configuration Summary:" -ForegroundColor Cyan - Write-Host " IP Address: $ipAddress/$prefixLength" -ForegroundColor White - if (![string]::IsNullOrWhiteSpace($gateway)) { - Write-Host " Gateway: $gateway" -ForegroundColor White - } - if ($dnsServers.Count -gt 0) { - Write-Host " DNS Servers: $($dnsServers -join ', ')" -ForegroundColor White - } - Write-Host "" - - $confirm = Read-Host "Apply this configuration? (Y/N)" - if ($confirm -ne 'Y' -and $confirm -ne 'y') { - Write-Host "Configuration cancelled." -ForegroundColor Yellow - Read-Host "Press Enter to continue" - return - } - - # Apply configuration - Write-Host "" - Write-Host "Applying configuration..." -ForegroundColor Yellow - - try { - # Remove existing IP configuration - Remove-NetIPAddress -InterfaceIndex $Adapter.InterfaceIndex -Confirm:$false -ErrorAction SilentlyContinue - Remove-NetRoute -InterfaceIndex $Adapter.InterfaceIndex -Confirm:$false -ErrorAction SilentlyContinue - - # Disable DHCP - Set-NetIPInterface -InterfaceIndex $Adapter.InterfaceIndex -Dhcp Disabled -ErrorAction Stop - - # Set IP Address - New-NetIPAddress -InterfaceIndex $Adapter.InterfaceIndex ` - -IPAddress $ipAddress ` - -PrefixLength $prefixLength ` - -DefaultGateway $(if (![string]::IsNullOrWhiteSpace($gateway)) { $gateway } else { $null }) ` - -ErrorAction Stop | Out-Null - - # Set DNS Servers - if ($dnsServers.Count -gt 0) { - Set-DnsClientServerAddress -InterfaceIndex $Adapter.InterfaceIndex ` - -ServerAddresses $dnsServers ` - -ErrorAction Stop - } - - Write-Host "Configuration applied successfully!" -ForegroundColor Green - } - catch { - Write-Host "Error applying configuration: $($_.Exception.Message)" -ForegroundColor Red - } - - Write-Host "" - Read-Host "Press Enter to continue" +# Import module +$scriptPath = Split-Path -Parent $PSCommandPath +$modulePath = Join-Path $scriptPath 'NetworkAdapter.psm1' +if (Test-Path $modulePath) { + Import-Module $modulePath -Force +} else { + Write-Host "Module not found: $modulePath" -ForegroundColor Red + Read-Host "Press Enter to exit" + exit } # Main script @@ -490,6 +40,7 @@ try { Write-Host "2. Set Manual IP Configuration" -ForegroundColor White Write-Host "3. Refresh Adapter Status" -ForegroundColor White Write-Host "4. Choose Different Adapter" -ForegroundColor White + Write-Host "5. Presets (save/load)" -ForegroundColor White Write-Host "0. Exit" -ForegroundColor Gray Write-Host "" @@ -510,6 +61,9 @@ try { '4' { break } + '5' { + Show-PresetsMenu -Adapter $adapter + } '0' { exit } diff --git a/network-presets.schema.json b/network-presets.schema.json new file mode 100644 index 0000000..f3a0ee7 --- /dev/null +++ b/network-presets.schema.json @@ -0,0 +1,35 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Network Presets Schema", + "type": "object", + "properties": { + "presets": { + "type": "array", + "items": { + "type": "object", + "properties": { + "adapterGuid": { "type": "string" }, + "adapterName": { "type": "string" }, + "adapterDescription": { "type": "string" }, + "presets": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "type": { "type": "string", "enum": ["Manual","DHCP"] }, + "ipAddress": { "type": "string" }, + "prefixLength": { "type": "integer", "minimum": 0, "maximum": 32 }, + "gateway": { "type": ["string","null"] }, + "dns": { "type": "array", "items": { "type": "string" } } + }, + "required": [ "name", "type" ] + } + } + }, + "required": [ "adapterGuid", "presets" ] + } + } + }, + "required": [ "presets" ] +} diff --git a/test-syntax.ps1 b/test-syntax.ps1 new file mode 100644 index 0000000..d8f9e0e --- /dev/null +++ b/test-syntax.ps1 @@ -0,0 +1,12 @@ +$files = @('NetworkAdapter.psm1','Set-NetworkAdapter.ps1') +foreach ($f in $files) { + Write-Host '---' $f '---' + $errors = $null + [void][System.Management.Automation.Language.Parser]::ParseFile((Join-Path $PSScriptRoot $f), [ref]$null, [ref]$errors) + if ($errors -and $errors.Count -gt 0) { + $errors | ForEach-Object { Write-Host ("Line {0}: {1}" -f $_.Extent.StartLineNumber, $_.Message) -ForegroundColor Red } + } + else { + Write-Host 'No syntax errors.' -ForegroundColor Green + } +}