I decided that I wanted a way to view all of the hosts file on a bunch of servers and see just what is listed. Most of the code is fairly straight-forward where I use Test-Connection and Test-Path to validate whether a system is online and then whether the Hosts file is under a WinNT folder or Windows folder. If a system was upgraded, there is potential that the Hosts file resides under WinNT.
Once the validation is done and everything is still checking out OK, I can then grab the contents of the file using the Switch command and the –file and –regex switches. By doing this, I am going to read each line of the file one line at a time. By supplying the –regex switch, I can define a regular expression to look for a specific item, in this case, the initial IPV4 IP address using this line:
"^\d\w+"
Yes, this is a weak regular expression for finding the IPV4 address, but I felt it was all I needed because there would only be a few things in the hosts file to find.
Also, since everything in that line is one big string, I have to perform a split in order to grab exactly what I need to include the IP address, hostname associated with the IP and any notes that happen to be with that information. I also clear out any extra spaces so the array created is always accurate when I add the items into the collection.
$new = $_.Split("") | ? {$_ -ne ""}
After which then everything else goes into the Default grouping and I then perform an If statement to verify that the is no comment block (#) and no white space on the hosts file using the following line:
If (!("\s+" -match $_ -OR $_.StartsWith("#"))) {
If it has neither of those, then it is assumed to be an IPV6 address and adds it to the collection. I was going to write a regular expression for IPV6 addresses, but since there are a few different variations of how an IPV6 address can be, I decided to just go with my original idea of looking for everything but the IPV6 and then setting the Default action of the switch to be the IPV6 address. For an idea of what a regular expression would look like on IPV6, check this out.
Here is an example of using the function:
As you can see, the output shows the Computer, IPV4, IPV6 , the associated hostname for the IP and any notes that might have been left on the Hosts file. Nothing too wild going on here. But running it on your enterprise network may or may not reveal some surprises that you were not expecting.
You can scan your network and then export the results to a CSV report by piping the output to Export-CSV.
Code
Function Get-HostsFile { <# .SYNOPSIS Retrieves the contents of a hosts file on a specified system .DESCRIPTION Retrieves the contents of a hosts file on a specified system .PARAMETER Computer Computer name to view host file from .NOTES Name: Get-HostsFile Author: Boe Prox DateCreated: 15Mar2011 .LINK https://boeprox.wordpress.com .EXAMPLE Get-HostsFile "server1" Description ----------- Retrieves the contents of the hosts file on 'server1' #> [cmdletbinding( DefaultParameterSetName = 'Default', ConfirmImpact = 'low' )] Param( [Parameter( ValueFromPipeline = $True)] [string[]]$Computer ) Begin { $psBoundParameters.GetEnumerator() | % { Write-Verbose "Parameter: $_" } If (!$PSBoundParameters['computer']) { Write-Verbose "No computer name given, using local computername" [string[]]$computer = $Env:Computername } $report = @() } Process { Write-Verbose "Starting process of computers" ForEach ($c in $computer) { Write-Verbose "Testing connection of $c" If (Test-Connection -ComputerName $c -Quiet -Count 1) { Write-Verbose "Validating path to hosts file" If (Test-Path "\\$c\C$\Windows\system32\drivers\etc\hosts") { Switch -regex -file ("\\$c\c$\Windows\system32\drivers\etc\hosts") { "^\d\w+" { Write-Verbose "Adding IPV4 information to collection" $temp = "" | Select Computer, IPV4, IPV6, Hostname, Notes $new = $_.Split("") | ? {$_ -ne ""} $temp.Computer = $c $temp.IPV4 = $new[0] $temp.HostName = $new[1] If ($new[2] -eq $Null) { $temp.Notes = "NA" } Else { $temp.Notes = $new[2] } $report += $temp } Default { If (!("\s+" -match $_ -OR $_.StartsWith("#"))) { Write-Verbose "Adding IPV6 information to collection" $temp = "" | Select Computer, IPV4, IPV6, Hostname, Notes $new = $_.Split("") | ? {$_ -ne ""} $temp.Computer = $c $temp.IPV6 = $new[0] $temp.HostName = $new[1] If ($new[2] -eq $Null) { $temp.Notes = "NA" } Else { $temp.Notes = $new[2] } $report += $temp } } } }#EndIF ElseIf (Test-Path "\\$c\C$\WinNT\system32\drivers\etc\hosts") { Switch -regex -file ("\\$c\c$\WinNT\system32\drivers\etc\hosts") { "^#\w+" { } "^\d\w+" { Write-Verbose "Adding IPV4 information to collection" $temp = "" | Select Computer, IPV4,IPV6, Hostname, Notes $new = $_.Split("") | ? {$_ -ne ""} $temp.Computer = $c $temp.IPV4 = $new[0] $temp.HostName = $new[1] If ($new[2] -eq $Null) { $temp.Notes = "NA" } Else { $temp.Notes = $new[2] } $report += $temp } Default { If (!("\s+" -match $_ -OR $_.StartsWith("#"))) { Write-Verbose "Adding IPV6 information to collection" $temp = "" | Select Computer, IPV4, IPV6, Hostname, Notes $new = $_.Split("") | ? {$_ -ne ""} $temp.Computer = $c $temp.IPV6 = $new[0] $temp.HostName = $new[1] If ($new[2] -eq $Null) { $temp.Notes = "NA" } Else { $temp.Notes = $new[2] } $report += $temp } } } }#End ElseIf Else { Write-Verbose "No host file found" $temp = "" | Select Computer, IPV4, IPV6, Hostname, Notes $temp.Computer = $c $temp.IPV4 = "NA" $temp.IPV6 = "NA" $temp.Hostname = "NA" $temp.Notes = "Unable to locate host file" $report += $temp }#End Else } Else { Write-Verbose "No computer found" $temp = "" | Select Computer, IPV4, IPV6, Hostname, Notes $temp.Computer = $c $temp.IPV4 = "NA" $temp.IPV6 = "NA" $temp.Hostname = "NA" $temp.Notes = "Unable to locate Computer" $report += $temp } } } End { Write-Output $report } }
Nice 🙂
Thanks, Dave!
Nice. One thing you might add is a check of
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\DataBasePath.
The default location is Windows\system32\drivers\etc\hosts, but it can be changed to another location using that registry key.
Nice! I didn’t think about that. I’ll add that into the function as a check to make sure it hasn’t changed locations. Thanks!
Awesome! Now how about (Add|Update|Remove)-HostFileEntry?
Thanks, I started on something like that to Add/Remove but then got sidetracked on something else. Hopefully I can come back to it and finish it up and post the code.