Skip to content

Commit 3a17d4f

Browse files
🌟 [Major]: Add functionality that gets local IP configuration (#2)
## Description This pull request introduces a new function for retrieving network interface IP configuration details. It also updates the test suite to validate the new network configuration functionality. **New network configuration functionality:** * Added the `Get-NetIPConfiguration` function which retrieves detailed IP configuration for all network interfaces, including support for filtering by operational status and address family. It outputs structured objects with interface, IP, subnet mask, gateway, and DNS information. **Test suite updates:** * Added new tests for `Get-NetIPConfiguration` and its alias `IPConfig`, ensuring the new functionality is properly validated. ## Type of change <!-- Use the check-boxes [x] on the options that are relevant. --> - [ ] 📖 [Docs] - [ ] 🪲 [Fix] - [ ] 🩹 [Patch] - [ ] ⚠️ [Security fix] - [ ] 🚀 [Feature] - [x] 🌟 [Breaking change] ## Checklist <!-- Use the check-boxes [x] on the options that are relevant. --> - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent a49928d commit 3a17d4f

7 files changed

Lines changed: 308 additions & 21 deletions

File tree

src/classes/public/IPConfig.ps1

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
class IPConfig {
2+
# The interface name
3+
[string] $InterfaceName
4+
5+
# The interface description
6+
[string] $Description
7+
8+
# The interface status
9+
[System.Net.NetworkInformation.OperationalStatus] $Status
10+
11+
# The address family
12+
[string] $AddressFamily
13+
14+
# The IP address
15+
[string] $IPAddress
16+
17+
# The prefix length
18+
[int] $PrefixLength
19+
20+
# The subnet mask
21+
[string] $SubnetMask
22+
23+
# The gateway
24+
[string] $Gateway
25+
26+
# The DNS servers
27+
[string] $DNSServers
28+
29+
IPConfig(
30+
[System.Net.NetworkInformation.NetworkInterface] $Interface,
31+
[System.Net.NetworkInformation.UnicastIPAddressInformation] $AddressInformation,
32+
[System.Net.NetworkInformation.IPInterfaceProperties] $InterfaceProperties
33+
) {
34+
$this.InterfaceName = $Interface.Name
35+
$this.Description = $Interface.Description
36+
$this.Status = $Interface.OperationalStatus
37+
switch ($AddressInformation.Address.AddressFamily) {
38+
([System.Net.Sockets.AddressFamily]::InterNetwork) { $this.AddressFamily = 'IPv4'; break }
39+
([System.Net.Sockets.AddressFamily]::InterNetworkV6) { $this.AddressFamily = 'IPv6'; break }
40+
default { $this.AddressFamily = $AddressInformation.Address.AddressFamily.ToString() }
41+
}
42+
$this.IPAddress = $AddressInformation.Address.IPAddressToString
43+
$this.PrefixLength = $AddressInformation.PrefixLength
44+
45+
if ($AddressInformation.Address.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetwork) {
46+
$this.SubnetMask = [IPConfig]::ConvertPrefixToMask($AddressInformation.PrefixLength)
47+
} else {
48+
# IPv6 masks are represented by prefix length
49+
$this.SubnetMask = $null
50+
}
51+
52+
$this.Gateway = ($InterfaceProperties.GatewayAddresses | ForEach-Object { $_.Address.IPAddressToString }) -join ', '
53+
$this.DNSServers = ($InterfaceProperties.DnsAddresses | ForEach-Object { $_.IPAddressToString }) -join ', '
54+
}
55+
56+
hidden static [string] ConvertPrefixToMask([int] $prefixLength) {
57+
if ($prefixLength -le 0) { return '0.0.0.0' }
58+
if ($prefixLength -ge 32) { return '255.255.255.255' }
59+
60+
[int[]] $octets = 0, 0, 0, 0
61+
$bits = $prefixLength
62+
for ($i = 0; $i -lt 4; $i++) {
63+
$take = [Math]::Min(8, $bits)
64+
if ($take -le 0) {
65+
$octets[$i] = 0
66+
} else {
67+
$octets[$i] = 255 - ([math]::Pow(2, (8 - $take)) - 1)
68+
}
69+
$bits -= $take
70+
}
71+
return ($octets -join '.')
72+
}
73+
}

src/formats/IPConfig.Format.ps1xml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Configuration>
3+
<ViewDefinitions>
4+
<View>
5+
<Name>IPConfigTable</Name>
6+
<ViewSelectedBy>
7+
<TypeName>IPConfig</TypeName>
8+
</ViewSelectedBy>
9+
<TableControl>
10+
<TableHeaders>
11+
<TableColumnHeader>
12+
<Label>Name</Label>
13+
</TableColumnHeader>
14+
<TableColumnHeader>
15+
<Label>Status</Label>
16+
</TableColumnHeader>
17+
<TableColumnHeader>
18+
<Label>Type</Label>
19+
</TableColumnHeader>
20+
<TableColumnHeader>
21+
<Label>Address</Label>
22+
</TableColumnHeader>
23+
<TableColumnHeader>
24+
<Label>Gateway</Label>
25+
</TableColumnHeader>
26+
<TableColumnHeader>
27+
<Label>DNSServers</Label>
28+
</TableColumnHeader>
29+
</TableHeaders>
30+
<TableRowEntries>
31+
<TableRowEntry>
32+
<TableColumnItems>
33+
<TableColumnItem>
34+
<PropertyName>Name</PropertyName>
35+
</TableColumnItem>
36+
<TableColumnItem>
37+
<PropertyName>Status</PropertyName>
38+
</TableColumnItem>
39+
<TableColumnItem>
40+
<PropertyName>Type</PropertyName>
41+
</TableColumnItem>
42+
<TableColumnItem>
43+
<PropertyName>IPAddress</PropertyName>
44+
</TableColumnItem>
45+
<TableColumnItem>
46+
<PropertyName>Gateway</PropertyName>
47+
</TableColumnItem>
48+
<TableColumnItem>
49+
<PropertyName>DNSServers</PropertyName>
50+
</TableColumnItem>
51+
</TableColumnItems>
52+
</TableRowEntry>
53+
</TableRowEntries>
54+
</TableControl>
55+
</View>
56+
</ViewDefinitions>
57+
</Configuration>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
function Get-SubnetMaskFromPrefix {
2+
<#
3+
.SYNOPSIS
4+
Converts a CIDR prefix length into a subnet mask in dotted decimal notation.
5+
6+
.DESCRIPTION
7+
The Get-SubnetMaskFromPrefix function accepts an integer prefix (e.g., 24) and converts it into a corresponding
8+
subnet mask (e.g., 255.255.255.0). It supports prefix lengths from 0 through 32. If the input prefix is outside
9+
this valid range, the function returns `$null`. This is useful when translating CIDR-style network definitions
10+
into traditional subnet mask format.
11+
12+
.EXAMPLE
13+
Get-SubnetMaskFromPrefix -prefix 24
14+
15+
Output:
16+
```powershell
17+
255.255.255.0
18+
```
19+
20+
Converts a /24 prefix to the subnet mask 255.255.255.0.
21+
22+
.OUTPUTS
23+
System.String
24+
25+
.NOTES
26+
The subnet mask string in dotted decimal format (e.g., 255.255.255.0).
27+
Returns `$null` if the prefix is not within the valid range (0–32).
28+
29+
.LINK
30+
https://psmodule.io/Net/Functions/Get-SubnetMaskFromPrefix
31+
#>
32+
[OutputType([string])]
33+
[CmdletBinding()]
34+
param(
35+
# The CIDR prefix length (0–32) to convert into a subnet mask.
36+
[Parameter(Mandatory)]
37+
[int] $prefix
38+
)
39+
40+
if ($prefix -lt 0 -or $prefix -gt 32) { return $null }
41+
42+
$bytes = [byte[]](0..3 | ForEach-Object {
43+
# Calculate the number of subnet bits for this octet (max 8, min 0)
44+
$bits = [Math]::Max([Math]::Min($prefix - (8 * $_), 8), 0)
45+
if ($bits -le 0) {
46+
# If no bits are set for this octet, value is 0
47+
0
48+
} elseif ($bits -ge 8) {
49+
# If all bits are set for this octet, value is 255
50+
255
51+
} else {
52+
# For partial octets, shift 0xFF left by (8 - $bits) to set the correct number of bits,
53+
# then mask with 0xFF to ensure only 8 bits are used
54+
((0xFF -shl (8 - $bits)) -band 0xFF)
55+
}
56+
})
57+
[System.Net.IPAddress]::new($bytes).ToString()
58+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
function Get-NetIPConfiguration {
2+
<#
3+
.SYNOPSIS
4+
Retrieves IP configuration details for network interfaces on the system.
5+
6+
.DESCRIPTION
7+
This function gathers IP configuration data, including IP addresses, subnet masks, gateway addresses,
8+
and DNS servers for all network interfaces. It supports optional filtering by interface operational status
9+
(Up or Down) and address family (IPv4 or IPv6). The output includes detailed per-address information in
10+
a structured object format for each network interface and IP address combination.
11+
12+
.EXAMPLE
13+
Get-NetIPConfiguration
14+
15+
Output:
16+
```powershell
17+
InterfaceName : Ethernet
18+
Description : Intel(R) Ethernet Connection
19+
Status : Up
20+
AddressFamily : InterNetwork
21+
IPAddress : 192.168.1.10
22+
PrefixLength : 24
23+
SubnetMask : 255.255.255.0
24+
Gateway : 192.168.1.1
25+
DNSServers : 8.8.8.8, 1.1.1.1
26+
```
27+
28+
Retrieves the IPv4 configuration for all network interfaces that are currently operational (Up).
29+
30+
.OUTPUTS
31+
IPConfig
32+
33+
.LINK
34+
https://psmodule.io/Net/Functions/Get-NetIPConfiguration
35+
#>
36+
37+
[Alias('IPConfig')]
38+
[OutputType([IPConfig])]
39+
[CmdletBinding()]
40+
param(
41+
# Filters interfaces based on operational status ('Up' or 'Down')
42+
[Parameter()]
43+
[ValidateSet('Up', 'Down')]
44+
[string] $InterfaceStatus,
45+
46+
# Filters IP addresses by address family ('IPv4' or 'IPv6')
47+
[Parameter()]
48+
[ValidateSet('IPv4', 'IPv6')]
49+
[string] $AddressFamily
50+
)
51+
52+
# Map AddressFamily parameter to .NET enum
53+
$familyEnum = $null
54+
if ($AddressFamily) {
55+
$familyEnum = if ($AddressFamily -eq 'IPv4') {
56+
[System.Net.Sockets.AddressFamily]::InterNetwork
57+
} else {
58+
[System.Net.Sockets.AddressFamily]::InterNetworkV6
59+
}
60+
}
61+
62+
$interfaces = [System.Net.NetworkInformation.NetworkInterface]::GetAllNetworkInterfaces()
63+
64+
# Apply optional interface status filter using enum for robustness
65+
if ($InterfaceStatus) {
66+
$statusEnum = [System.Net.NetworkInformation.OperationalStatus]::$InterfaceStatus
67+
$interfaces = $interfaces | Where-Object { $_.OperationalStatus -eq $statusEnum }
68+
}
69+
70+
foreach ($adapter in $interfaces) {
71+
$ipProps = $adapter.GetIPProperties()
72+
73+
# Filter unicast addresses by address family if requested
74+
$unicast = $ipProps.UnicastAddresses
75+
if ($familyEnum) {
76+
$unicast = $unicast | Where-Object { $_.Address.AddressFamily -eq $familyEnum }
77+
}
78+
79+
foreach ($addr in $unicast) {
80+
[IPConfig]::new($adapter, $addr, $ipProps)
81+
}
82+
}
83+
}

src/functions/public/Test-PSModuleTest.ps1

Lines changed: 0 additions & 18 deletions
This file was deleted.

src/types/IPConfig.Types.ps1xml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Types>
3+
<Type>
4+
<Name>IPConfig</Name>
5+
<Members>
6+
<AliasProperty>
7+
<Name>Type</Name>
8+
<ReferencedMemberName>AddressFamily</ReferencedMemberName>
9+
</AliasProperty>
10+
<AliasProperty>
11+
<Name>Name</Name>
12+
<ReferencedMemberName>InterfaceName</ReferencedMemberName>
13+
</AliasProperty>
14+
<AliasProperty>
15+
<Name>Mask</Name>
16+
<ReferencedMemberName>PrefixLength</ReferencedMemberName>
17+
</AliasProperty>
18+
</Members>
19+
</Type>
20+
</Types>
Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,22 @@
1919
[CmdletBinding()]
2020
param()
2121

22-
Describe 'Module' {
23-
It 'Function: Test-PSModuleTest' {
24-
Test-PSModuleTest -Name 'World' | Should -Be 'Hello, World!'
22+
Describe 'Net' {
23+
Context 'Get-NetIPConfiguration' {
24+
It 'returns expected results' {
25+
$results = Get-NetIPConfiguration
26+
LogGroup 'Results' {
27+
Write-Host "$($results | Out-String)"
28+
}
29+
$results | Should -BeOfType 'IPConfig'
30+
}
31+
32+
It 'IPConfig alias works' {
33+
$results = IPConfig
34+
LogGroup 'Results' {
35+
Write-Host "$($results | Format-List | Out-String)"
36+
}
37+
$results | Should -BeOfType 'IPConfig'
38+
}
2539
}
2640
}

0 commit comments

Comments
 (0)