February 2012 - Posts

Regardless of whether a server is for Exchange, for SQL, or for any other use; a key component of the server's configuration is the set of IP addresses assigned to it.

Many organizations apply all addresses (even server addresses) using DHCP. For servers, this is often based on reservations of the MAC addresses on those servers. In most cases, this works well. However, if the DHCP server(s) should be unavailable (for whatever reason), the server will not receive the desired address, and instead will receive an APIPA address (that is, an IPv4 address starting with "169." as the first octet of the IPv4 address).

Regardless of the result, for many servers it is important to know if the IP addresses should change. The first step in that process is to be able to enumerate (list) all of the IP addresses for all of the network interfaces on the server.

There are a number of mechanisms available for obtaining this information from Windows; including but not limited to "ipconfig.exe", WMI, "wmic.exe", "netsh.exe", and others. The challenge for using most of the available interfaces is that they provide a significant amount of extraneous information. Extraneous information makes the output very difficult to parse and use within scripts.

This blog post provides a script that enumerates all of the network interfaces available on a particular computer and the associated IP addresses. The second script, which you could schedule to run every 15-30 minutes, compares and contrasts any changes in IP addresses on a server and reports the change via a SMTP server (such as Exchange Server).

The first script:

###
### IP-Address-Check.ps1
###
### This script lists all Ethernet adapters supporting IP on a computer and
### the associated IP addresses - both IPv4 and IPv6. A simple object is
### output containing the name of the interface, an array containing the IPv4
### addresses, and an array containing the IPv6 addresses.
###
### While this information is available from netsh.exe, the format of the
### output from netsh.exe is not conducive for easy re-use or parsing.
###
### Michael B. Smith
### michael at smithcons dot com
### http://TheEssentialExchange.com
### February 28, 2012
###

Set-StrictMode -Version 2.0

$HKLM = 2147483650
$wmi  = [wmiclass]"\root\default:StdRegProv"

function RegRead
{
	Param(
		[int64] $hive,
		[string]$keyName,
		[string]$valueName,
		[ref]   $value,
		[string]$type = "REG_SZ"
	)

	switch ( $type )
	{
		'reg_sz'
			{
				$r = $wmi.GetStringValue( $hive, $keyName, $valueName )
				$value.Value = $r.sValue
			}
		'reg_dword'
			{
				$r = $wmi.GetDWORDValue( $hive, $keyName, $valueName )
				$value.Value = $r.uValue
			}
		'reg_qword'
			{
				$r = $wmi.GetQWORDValue( $hive, $keyName, $valueName )
				$value.Value = $r.uValue
			}
		'reg_binary'
			{
				$r = $wmi.GetBinaryValue( $hive, $keyName, $valueName )
				$value.Value = $r.uValue
			}
		default
			{
				write-error "ERROR: RegRead $hive $keyName $valueName"
				return 1
			}
	}

	return $r.ReturnValue
}

function EnumerateAdapters
{
	$nicSettings = gwmi Win32_NetworkAdapterSetting -EA 0
	foreach( $nicSetting in $nicSettings )
	{
		$nicElement = [wmi] $nicSetting.Element
		if( $nicElement.AdapterType -eq "Ethernet 802.3" )
		{
			$nicConfig = [wmi] $nicSetting.Setting ## is of type Win32_NetworkAdapterConfiguration
			$nicGUID = $nicConfig.SettingID
			if( $nicConfig.IPEnabled -eq $true )
			{
				$hive = $HKLM
				$keyName = "System\CurrentControlSet\Control\Network\" +
					"{4D36E972-E325-11CE-BFC1-08002BE10318}\" + 
					$nicGUID + 
					"\Connection"
				$valueName = "Name"

				$name = ''
				$result = RegRead $hive $keyName $valueName ( [ref] $name ) 'reg_sz'
				if( $result -ne 0 )
				{
					$name = ""
				}

				$arrIPListPublic_v6 = @()
				$arrIPListPublic_v4 = @()

				foreach( $ip in $nicConfig.IPAddress )
				{
					if( $ip.IndexOf( ':' ) -ge 0 )
					{
						$arrIPListPublic_v6 += $ip
					}
					else
					{
						$arrIPListPublic_v4 += $ip
					}
				}

				$obj = "" | select Name, IPv4Addresses, IPv6Addresses
				$obj.Name = $name
				$obj.IPv4Addresses = $arrIPListPublic_v4
				$obj.IPv6Addresses = $arrIPListPublic_v6

				$obj  ### output the object to the pipeline

				$arrIPListPublic_v4 = $null
				$arrIPListPublic_v6 = $null
			}

			$nicConfig = $null
		}
		$nicElement = $null
	}
	$nicSettings = $null
}

###
### Main
###

EnumerateAdapters

The second script:

###
### IP-Check-Controller.ps1
###
### Check to see if any IP addresses on a computer have changed. This may 
### be relevant in many configurations based on DHCP.
###
### Michael B. Smith
### michael at smithcons dot com
### http://TheEssentialExchange.com
### February 28, 2012
###

Param(
	[string]$to  = "ip-report@example.com",
	[string]$mx  = "mx.example.com",
	[string]$sub = "The IP address information has changed at $($env:ComputerName)",
	[string]$fr  = "ip-check-controller@example.local"
)

[string]$nl = "`r`n"

###
### Main
###

if( ( Test-Path "Addr-old.txt" -PathType Leaf ) )
{
	rm "Addr-old.txt"
}
if( ( Test-Path "Addr-new.txt" -PathType Leaf ) )
{
	mv "Addr-new.txt" "Addr-old.txt"
}

.\IP-Address-Check | out-file "Addr-new.txt"

$result = Compare-Object -ReferenceObject $(gc "Addr-old.txt") -DifferenceObject $(gc "Addr-new.txt") -PassThru
if( $result -eq $null )
{
	## no changes to file
	exit
}

### if we get here, the files are different

$text =
	"A change has occurred in the IP addresses or active adapters.$nl$nl"+
	"The changes found are:$nl"

foreach( $line in $result )
{
	$text += "`t" + $line + $nl
}

$text += $nl + "The new full configuration is:$nl"
	$lines = gc "Addr-New.txt"
	foreach( $line in $lines )
	{
		$text += "`t$line$nl"
	}

$text += $nl + "The old full configuration is:$nl"
	$lines = gc "Addr-Old.txt"
	foreach( $line in $lines )
	{
		$text += "`t$line$nl"
	}

Send-MailMessage -To $to -Subject $sub -From $fr -Body $text -SmtpServer $mx -Priority High

Typically, instead of scheduling the second PowerShell script, you would schedule a BAT script, as shown below:

@echo off
REM
REM IP-Check-Controller.bat
REM
REM Michael B. Smith
REM michael at smithcons dot com
REM http://TheEssentialExchange.com
REM February 28, 2012
REM

powershell.exe -command ".\IP-Check-Controller.ps1"

Until next time...

If there are things you would like to see written about, please let me know.