3

I'm looking for a way to filter new IP-Addresses in my host file. I created a script which updates my host file every time when I call it with data from the matrix enterprise manager. It works fine. But I have to find a solution that only 10.XX.XX.XX or 172.XX.XX.XX addresses are allowed to update.

Param(
  $newHost = $args[0],
  $newIP = $args[1]
)

$SourceFile = "hosts"
$Match = "$newHost"

(Get-Content $SourceFile) | % {if ($_ -notmatch $Match) {$_}} | Set-Content $SourceFile

Start-Sleep -Seconds 1

$tab = [char]9
$enter = $newIP + $tab + $newHost

if ($newIP XXXX)  #--> here should be a regex if condition... no clue how it works..

$enter | Add-Content -Path hosts
Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
olizimmermann
  • 74
  • 1
  • 2
  • 9

2 Answers2

8

Your code is unnecessarily convoluted and doesn't make proper use of the features PowerShell provides.

  • Don't assign $args[...] to parameters. That's not how parameter handling in PowerShell works. Make the parameters mandatory instead.
  • % {if ($_ -notmatch $Match) {$_}} is better phrased as Where-Object {$_ -notmatch $Match}.
  • If $Match is an FQDN the dots might cause false positives (because they match any character, not just literal dots). Either escape $Match ([regex]::Escape($Match)) or use the -notlike operator instead.
  • PowerShell has an escape sequence for tabs (`t). No need to define a variable with a value of [char]9.
  • Putting variables in a double-quoted string ("$var1$var2") is often more readable than string concatenation ($var1 + $var2).

Change your code to something like this:

[CmdletBinding()]
Param(
  [Parameter(Mandatory=$true)]
  [string]$Hostname,
  [Parameter(Mandatory=$true)]
  [string]$IPAddress
)

$SourceFile = 'hosts'

(Get-Content $SourceFile) |
  Where-Object { $_ -notlike "*$Hostname*" } |
  Set-Content $SourceFile

Start-Sleep -Seconds 1

if ($IPAddress -match '^(10|172)\.') {
  "$IPAddress`t$Hostname" | Add-Content $SourceFile
}

If you want to avoid writing the output file multiple times, you could collect the data read in a variable, and then write that variable and the new record in one go:

$hosts = @(Get-Content $SourceFile) | Where-Object { $_ -notlike "*$Hostname*" })

if ($IPAddress -match '^(10|172)\.') {
  $hosts += "$IPAddress`t$Hostname"
}

$hosts | Set-Content $SourceFile

You could further optimize your script by doing the check via parameter validation, so you don't need an if condition in the function body in the first place, e.g. like this:

Param(
  [Parameter(Mandatory=$true)]
  [string]$Hostname,
  [Parameter(Mandatory=$true)]
  [ValidatePattern('^(10|172)\.')]
  [string]$IPAddress
)

or like this:

Param(
  [Parameter(Mandatory=$true)]
  [string]$Hostname,
  [Parameter(Mandatory=$true)]
  [ValidateScript({$_ -match '^(10|172)\.' -and [bool][ipaddress]$_})]
  [string]$IPAddress
)
Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
3

Some of the comments above use regex to validate the entire IPv4 address. If you are confident that the IP address you are checking is valid, then you can use "^(10|172)\." to verify just the first octet of the address as per your question:

if($newIP -match "^(10|172)\.") { ... }

If you do want to validate the whole address, there is an alternative way to do this by converting $newIP to a [System.Net.IPAddress] type. If this fails the result will be nothing (null), which is implicitly false, so the following gives you a true/false check that a string is a valid IP address:

[bool]($newIP -as [System.Net.IPAddress])

You could use this to validate the input to the function written by Ansgar:

[Parameter(Mandatory=$true)]
[ValidateScript({[bool]($_ -as [System.Net.IPAddress]})
[string]$IPAddress
Charlie Joynt
  • 4,411
  • 1
  • 24
  • 46
  • Concerning the validation: just`[Parameter(Mandatory=$true)][System.Net.IPAddress]$IPAddress` is better and results in the following standard error when the type is incorrect: **Cannot process argument transformation on parameter 'IPAddress'. Cannot convert value "Test" to type "System.Net.IPAddress". Error: "An invalid IP address was specified."** – iRon Jun 08 '17 at 11:06
  • In this particular case (since the OP wants to allow only 2 specific /8 subnets, and the address is going to a text file anyway) I would consider a string parameter with a `ValidatePattern()` clause the better option. – Ansgar Wiechers Jun 08 '17 at 11:23
  • Ooh. `ValidatePattern()` is new to me. I think I learn more by answering questions than asking them. :-) – Charlie Joynt Jun 08 '17 at 13:18