Today’s blog focuses on reading the hosts file with PowerShell. Though there are many reasons that someone may modify the hosts file of a machine, it can easily get out of hand with entries scattered about your network.
Whatever the story, most of us have had reason to read or modify info in the hosts file at one point in our lives. We’re going to cover reading the hosts file with PowerShell locally as well as remotely for multiple machines. Yay!
Reading the hosts file with PowerShell
The hosts file is, simply put, a mapping of host names to corresponding IP addresses. You can go explore the how and the why of using it, but that’s outside the scope of this blog.
It’s easy enough to browse to the hosts file location and open the file manually. On modern versions of Windows, it’s stored here (you’ll need admin access to modify it):
%SystemRoot%\System32\drivers\etc\hosts
But, what about keeping tally of all the machines on your network? Do you really want to manually browse to this file on each machine? This is most definitely a rhetorical question. The correct answer is an emphatic, “No!”
So, what do we do? We use PowerShell of course! Reading the hosts file with PowerShell is a breeze.
Getting the entire contents of hosts file with PowerShell
In order to do this, we are going to make use of the Get-Content cmdlet. This cmdlet will read the contents of a file.
If you only need to quickly see the entire contents of the hosts file, then this super simple one-liner should do the trick:
Get-Content $env:SystemRoot\System32\Drivers\etc\hosts
That’s simple enough and gets us the basic info that we’re looking for.
But, what if we want to only grab the actual entries instead of the entire file? Easy peasy with PowerShell.
Getting all entries from hosts file
In order to get all the entries from the hosts file, we’re going to use regular expressions to compare against every line in the hosts file.
Additionally, we will also log the output to a file. Having a log file will make it much easier to see all the results at a glance
$LogFile = "\\MyScripts\Logs\hosts file results.csv"
$Pattern = '^(?<IP>\d{1,3}(\.\d{1,3}){3})\s+(?<Host>.+)$'
$File = "$env:SystemDrive\Windows\System32\Drivers\etc\hosts"
$Entries = @()
(Get-Content -Path $File) | ForEach-Object {
If ($_ -match $Pattern) {
$Entries += "$env:COMPUTERNAME,$($Matches.IP),$($Matches.Host)"
}
}
$Entries | Tee-Object -FilePath $LogFile -Append
Voila! It’s fairly simple and it gets you clean results with only the actual entries in the hosts file (rather than the entire file contents).
Fantastic! Now, let’s move on to doing this for all the machines!
Getting entries from hosts file for multiple machines
So, we’ve got this fancy script to get our hosts file entries and log them to a file. Getting them from multiple machines is also a snap.
Here are a few different ways that you can accomplish this:
PDQ Deploy and PowerShell steps
PDQ Inventory and Remote Command
Directly access the files on the remote computers via UNC path
PDQ Deploy and PowerShell steps
Here are the steps to use PDQ Deploy with these PowerShell scripts.
1. Create a PDQ Deploy Package with a PowerShell Step that includes your script.
2. Deploy to your targets and verify your log file.
Like magic, you can now deploy to all your targets and save your results to a log file on some UNC path. Go you!
PDQ Inventory and Remote Command
Here are the steps to use PDQ Inventory with these PowerShell scripts.
1. Select your targets and use Remote Command.
2. Save your PowerShell script to a file and call that file from PowerShell Remote Command.
3. Select all targets to see combined results. Computers with no results will simply show, “Return code: 0” since they successfully ran but didn’t have any results.
Ta da! It’s that easy. It’s very quick to select some machines and then see what they have in their hosts file.
Remote PowerShell and Invoke-Command
This requires that you have previously configured remote PowerShell for your targets. Otherwise, you’ll receive errors when attempting to connect to your targets. I’ve added Test-WSMan to filter those results.
Additionally, we’ll need to change how we’re logging to file otherwise we’ll run into double-hop authentication issues.
$LogFile = "\\MyScripts\Logs\hosts file results.csv"
$Result = @()
$Computers = @("FRY", "UTER", "WOLVERINE")
$MyScript = {
$Pattern = '^(?<IP>\d{1,3}(\.\d{1,3}){3})\s+(?<Host>.+)$'
$File = "$env:SystemDrive\Windows\System32\Drivers\etc\hosts"
(Get-Content -Path $File) | ForEach-Object {
If ($_ -match $Pattern) {
"$env:COMPUTERNAME,$($Matches.IP),$($Matches.Host)"
}
}
}
Foreach ($Computer in $Computers) {
If ( [bool](Test-WSMan -ComputerName $Computer -ErrorAction SilentlyContinue) ) {
$Result += Invoke-Command -ComputerName $Computer -ScriptBlock $MyScript
}
}
$Result | Tee-Object -FilePath $LogFile -Append
Directly read remote hosts file via UNC path
This method is simply using the direct UNC path to the hosts files. In this example, I’m assuming the system drive is C.
$LogFile = "\\MyScripts\Logs\hosts file results.csv"
$Pattern = '^(?<IP>\d{1,3}(\.\d{1,3}){3})\s+(?<Host>.+)$'
$Entries = @()
$Computers = @("FRY", "UTER", "WOLVERINE")
Foreach ($Computer in $Computers) {
$file = "\\$Computer\c$\Windows\System32\Drivers\etc\hosts"
(Get-Content -Path $File) | ForEach-Object {
If ($_ -match $Pattern) {
$Entries += "$computer,$($Matches.IP),$($Matches.Host)"
}
}
}
$Entries | Tee-Object -FilePath $LogFile -Append
This will probably be the fastest method to get the hosts file info.
Wrapping up
That’s it! Now, you have a bunch of ways to tackle the task of reading all your hosts file records.
These are just examples, so you’ll likely want to modify them for your environment. One change that I can think of, for example, is importing your list of computers. In my examples, I am using a static list of computers.
In any case, have fun with it! Happy PowerShelling!