The customer I am working, for now, asked me to search for a specific IP address in the hosts file on all the computers in the network. To achieve this goal I wrote a very quick-and-dirty PowerShell script:

$FileName = "C:\Windows\System32\drivers\etc\hosts"
$IPAdresses1 = "10.11.12.13"
$IPAdresses2 = "10.11.12.14"
$Directory = "\\server\clienthealth$\Logs"
$ComputerNames = @()

if ($Directory -ne "")
   {
    $ComputerNames    = Get-ChildItem -Path $Directory -Filter *.log | Where {$_.Name -notmatch "powershell"} | Sort-Object Path
   }

Clear-Host

$valCounter = 1
ForEach ($ComputerName in $ComputerNames)
 {

  $Computername = $ComputerName -replace(".log","")

  Write-Progress -Activity "Processing the computers" -Status "Processing computer $ComputerName" -PercentComplete ($valCounter / $ComputerNames.Count * 100)
  If(Test-Connection -Quiet -ComputerName $ComputerName -Count 1)
   {
    $LocalFileName = "\\"+$ComputerName + "\" + $FileName
    $LocalFileName = $LocalFileName -replace ":","$"
    $HostsFile = Get-Content -Path $LocalFileName | Select-String -Pattern $IPAdresses1,$IPAdresses2
    if($HostsFile -ne $Null)
     {
      Write-Host "$ComputerName has $HostsFile in the host file." 
      Write-Host "============================================================================================"
     }
   }
  $valCounter++
 }

We are using Anders Rodland Client Health script to keep our SCCM environment healthy. The log files are stored in the directory \\server\clienthealth$\logs. The log file name is equal to the computer name, with .log as the extension. So it was quite easy: just go through the log files, remove the .log part, check if the computer online, and in case yes, change C:\Windows\System32\drivers\etc\hosts to a UNC path, open it, and search for one of the IP addresses. If one of the IP addresses is found, display it on the screen. 

The usage of this script is very limited. So I added some more functionality:

  • Search not only in single files but also in multiple files in folders
  • Export the results to a CSV file
  • Mail the CSV file with the results if needed
  • Specify various options to specify the computer names:
    • All the computer names in a specific OU and its child OUs.
    • All the computer names specified in a text file
    • All the computer names that can be found in the Client Health logs directory.
    • All the computer names that are mentioned as a parameter

 

So there are various options to search in files for specific content. 

The script:

<#
.SYNOPSIS
    Finds entries in files on remote computers.

.DESCRIPTION
    Finds entries in files on remote computers and export the computername, currently logged on user, filename and
    the entry to a CSV file. 

.EXAMPLE
    Find the entry 10.12.14.15 in c:\windows\system32\drivers\etc\hosts on computers PC1 and PC2
     ."CheckFilesForEntries (v01).ps1" -LookFor 10.12.14.15 -FileName c:\windows\system32\drivers\etc\hosts -ComputerNames PC1,PC2

.EXAMPLE
    Find the entry 80004005 in all the log files in C:\windows\ccm\logs\*.log on computers PC1 and PC2
     ."CheckFilesForEntries (v01).ps1" -LookFor 80004005 -FileName c:\windows\ccm\logs\*.log -ComputerNames PC1,PC2

.EXAMPLE
    Find the entry 80004005 in all the files in C:\windows\ccm\logs\ on computers PC1 and PC2
     ."CheckFilesForEntries (v01).ps1" -LookFor 80004005 -FileName c:\windows\ccm\logs\ -ComputerNames PC1,PC2

.EXAMPLE
    Find the entry 10.12.14.15 in c:\windows\system32\drivers\etc\hosts on computers PC1 and PC2 and mail the CSV file. 
     ."CheckFilesForEntries (v01).ps1" -LookFor 10.12.14.15 -FileName c:\windows\system32\drivers\etc\hosts -ComputerNames PC1,PC2 -MailCSVFile

.EXAMPLE
    Find the entry's 10.12.14.15 or 10.12.14.16 in c:\windows\system32\drivers\etc\hosts on all the computernames which logfiles are in the folder \\server\share\ClientHealth\log
     ."CheckFilesForEntries (v01).ps1" -LookFor 10.12.14.15,10.12.14.15 -FileName c:\windows\system32\drivers\etc\hosts -Directory \\server\share\ClientHealth\log

.NOTES
    Author:  Willem-Jan Vroom
    Website: https://www.vroom.cc/
    Twitter: @TheStingPilot

v0.1:
   * Initial version.

#>

[CmdLetBinding(DefaultParameterSetName = 'Computers')]

param
  (
   [Parameter(HelpMessage='Specify the computer names, comma seperated.')]
   [Parameter(Mandatory=$true,  ParameterSetName = 'Computers')] 
   [string[]] $ComputerNames,

   [Parameter(HelpMessage='Specify a file name that contains all the computer names.')]
   [Parameter(Mandatory=$true,  ParameterSetName = 'FileWithComputerNames')]
   [String]   $Inputfile = "",

   [Parameter(HelpMessage='Specify the directory that contains all the computer names.')]
   [Parameter(Mandatory=$true,  ParameterSetName = 'DirWithComputerNames')]
   [String]   $Directory = "",

   [Parameter(HelpMessage='Specify the OU that contains all the computer names.')]
   [Parameter(Mandatory=$true,  ParameterSetName = 'OUWithComputerNames')]
   [String]   $OU = "",

   [Parameter(HelpMessage='Specify the log path.')]
   [Parameter(Mandatory=$false, ParameterSetName = 'Computers')]
   [Parameter(Mandatory=$false, ParameterSetName = 'FileWithComputerNames')]
   [Parameter(Mandatory=$false, ParameterSetName = 'DirWithComputerNames')]
   [Parameter(Mandatory=$false, ParameterSetName = 'OUWithComputerNames')]
   [String]   $LogPath = "",

   [Parameter(HelpMessage='Specify the full file names where should be searched in. If more than one file name, the seperate them by a comma.')]
   [Parameter(Mandatory=$true, ParameterSetName = 'Computers')]
   [Parameter(Mandatory=$true, ParameterSetName = 'FileWithComputerNames')]
   [Parameter(Mandatory=$true, ParameterSetName = 'DirWithComputerNames')]
   [Parameter(Mandatory=$false, ParameterSetName = 'OUWithComputerNames')]
   [String[]] $FileNames = "",

   [Parameter(HelpMessage='Specify what should the searched for. I more than one item, then seperate them by a comma.')]
   [Parameter(Mandatory=$true, ParameterSetName = 'Computers')]
   [Parameter(Mandatory=$true, ParameterSetName = 'FileWithComputerNames')]
   [Parameter(Mandatory=$true, ParameterSetName = 'DirWithComputerNames')]
   [Parameter(Mandatory=$false, ParameterSetName = 'OUWithComputerNames')]
   [String[]] $LookFor = "",

   [Parameter(HelpMessage='Mail the results file.')]
   [Parameter(Mandatory=$false, ParameterSetName = 'Computers')]
   [Parameter(Mandatory=$false, ParameterSetName = 'FileWithComputerNames')]
   [Parameter(Mandatory=$false, ParameterSetName = 'DirWithComputerNames')]
   [Parameter(Mandatory=$false, ParameterSetName = 'OUWithComputerNames')]
   [Switch]   $MailCSVFile

  )

# =============================================================================================================================================
# Function block
# =============================================================================================================================================

 Function Get-Site
 {
     <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       08-January-2019
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Get-Site
    =============================================================================================================================================
    .SYNOPSIS

    This function gets the site name of the given computer.

    SOURCE: https://www.powershellmagazine.com/2013/04/23/pstip-get-the-ad-site-name-of-a-computer/
    #>

    Param
     (
      $PC = ""
     )

    $RegKeyName   = "SYSTEM\CurrentControlSet\services\Netlogon\Parameters"
    $RegValueName = "DynamicSiteName"

    $Site = (([Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $PC)).OpenSubkey($RegKeyName)).GetValue($RegValueName)

    Return $Site    
    
 }


Function Get-LoggedOnUser
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       11-December-2019
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Get-LoggedOnUser
    =============================================================================================================================================
    .SYNOPSIS

    This function gets the current logged on user of a computer.

    #>

    param
     (
      [String] $Computer
     )
    Try
     {
      $UserName      = ((Get-WmiObject Win32_ComputerSystem -ComputerName $Computer -ErrorAction SilentlyContinue).UserName).Split("\")[-1]
      $DisplayName   = (Get-ADUser -Filter {SamAccountName -eq $UserName} -Properties DisplayName -ErrorAction SilentlyContinue).DisplayName
     }
      Catch
     {
      $DisplayName   = ""
     }
        
    Return $DisplayName
   }

Function Write-EntryToResultsFile
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       03-August-2018
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Write-EntryToResultsFile
    =============================================================================================================================================
    .SYNOPSIS

    This function adds the success or failure information to the array that contains the log
    information.

    #>

    Param
     (
      [String]  $PCName                  = "",
      [String]  $LocalFileName           = "",
      [String]  $FoundEntry              = "",
      [String]  $Line                    = "",
      [String]  $Remark                  = ""
     )

    $ADSite            = Get-Site -PC $PCName
    $UsernameToDisplay = Get-LoggedOnUser -Computer $PCName
    $Timestamp         = (Get-Date -UFormat "%a %e %b %Y %X").ToString()

    $Record  = [ordered] @{"Timestamp"                 = "";
                           "ComputerName"              = "";
                           "AD Site"                   = "";
                           "Username"                  = "";
                           "Local file name"           = "";
                           "Found entry"               = "";
                           "Line"                      = "";
                           "Remark"                    = ""
                           }
    
    $Record."Timestamp"                  = $Timestamp
    $Record."ComputerName"               = $PCName
    $Record."AD Site"                    = $ADSite
    $Record."Username"                   = $UsernameToDisplay
    $Record."Local file name"            = $LocalFileName
    $Record."Found entry"                = $FoundEntry
    $Record."Line"                       = $Line
    $Record."Remark"                     = $Remark
    $objRecord                           = New-Object PSObject -Property $Record
    $Global:arrTable                    += $objRecord

    Write-Verbose ">>  Write-EntryToResultsFile" 
    Write-Verbose "--> Entry written to the logfile:"
    Write-Verbose "     Timestamp                   = $Timestamp"
    Write-Verbose "     ComputerName                = $PCName"
    Write-Verbose "     AD Site                     = $ADSite"
    Write-Verbose "     Username                    = $UsernameToDisplay"
    Write-Verbose "     Local file name             = $LocalFileName"
    Write-Verbose "     Found entry                 = $FoundEntry"
    Write-Verbose "     Line                        = $Line"
    Write-Verbose "     Remark                      = $Remark"
    Write-Verbose ""
    Write-Verbose "#################################################################"

   }

  Function Export-ResultsLogFileToCSV
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       06-September-2018
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Export-ResultsLogFileToCSV
    =============================================================================================================================================
    .SYNOPSIS

    This function writes the logfile content to a CSV file.

    #>

    If($Global:arrTable.Count -gt 0)
     {
      $Global:arrTable | Sort-Object -Property ComputerName | Export-Csv $strCSVLogFileSucces -NoTypeInformation
      Write-Verbose ">> Export-ResultsLogFileToCSV: The file '$strCSVLogFileSucces' has been written." 
     } 
      else
     {
      Write-Error "Something went wrong while writing the logfile '$strCSVLogFileSucces'. Maybe nothing to report..." -Category CloseError 
     } 
   }
 
 Function Convert-ToUNCShare

   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       02-December-2019
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Convert-ToUNCShare
    =============================================================================================================================================
    .SYNOPSIS

    Converts a given filepath to a UNC share.

    #>

    Param
     (
      [String]  $ComputerName,
      [String]  $FilePath
     )
  
    $tmpVar = "\\"+ $ComputerName + "\" + $FilePath.Replace(":","$")
    Return $tmpVar
  
  }         

Function Convert-BackFromUNCShare

  {
 
   <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       02-December-2019
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Convert-BackFromUNCShare
    =============================================================================================================================================
    .SYNOPSIS

    Converts a given UNC file name back to a local name

    #>

    Param
     (
      [String]  $ComputerName,
      [String]  $UNCFilePath
     )
  
    $tmpVar = $UNCFilePath.replace("\\"+$ComputerName+"\","").Replace("$",":")
    Return $tmpVar

  }
  
Function Mail-CSVResultsFile
  {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       09-January-2020
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Mail-CSVResultsFile
    =============================================================================================================================================
    .SYNOPSIS

    Mails the CSV file with the results.

    #>

    Param
     (
      [String]    $Sendername,
      [String[]]  $Recipients,
      [String]    $Attachment,
      [String]    $Subject = "Overview",
      [String]    $SMTPServer,
      [String]    $SMTPPort = 25,
      [Switch]    $SSL,
      [Switch]    $Anonymous
     )

   $SMTPCredentials = ""
   $AttachmentFileName = $Attachment.Split("\")[-1]
   if(-not($Anonymous))
    {
    $SMTPCredentials = (Get-Credential)
    }

   $MessageBody = "Hello, 
   
   Please find attached the latest overview of $AttachmentFileName.
   
   With kind regards,
   Willem-Jan Vroom
   "

   if($SSL)
    {
     Write-Verbose "Mail via SSL"
     Send-MailMessage -From $Sendername            `
                      -To $Recipients              `
                      -SmtpServer $SMTPServer      `
                      -Attachments $Attachment     `
                      -Subject $Subject            `
                      -Body $MessageBody           `
                      -Port $SMTPPort              `
                      -Credential $SMTPCredentials `
                      -UseSSL
    }
   else
    {
     Write-Verbose "Mail not via SSL"
     Send-MailMessage -From $Sendername            `
                      -To $Recipients              `
                      -SmtpServer $SMTPServer      `
                      -Attachments $Attachment     `
                      -Subject $Subject            `
                      -Body $MessageBody           `
                      -Port $SMTPPort              `
                      -Credential $SMTPCredentials 
    }

  }

# =============================================================================================================================================
# End function block
# =============================================================================================================================================

# =============================================================================================================================================
# Define the variables
# =============================================================================================================================================

  Clear-Host
  
  $Global:arrTable   = @()
  $Results           = @()
  $valCounter        = 1
  $bolMaxComputers   = $False

  if($LogPath -eq "")
   {
    $LogPath   = Split-Path -parent $MyInvocation.MyCommand.Definition
   }
    else
   {
    if(-not(Test-Path($LogPath)))
     {
      New-Item -Path $LogPath | Out-Null
      Write-Verbose "The folder '$LogPath' has been created."
     } 
   }

# =============================================================================================================================================
# Read the file with all the computer names
# =============================================================================================================================================
 
  if($Inputfile -ne "")
   {
    if(Test-Path($Inputfile))
     { 
      $ComputerNames = @(Get-Content -Path $Inputfile)
      Write-Verbose "The inputfile '$Inputfile' has been read."
      Write-Verbose "The following $($ComputerNames.Count) will be processed:"
      ForEach ($ComputerName in $ComputerNames)
       {
        Write-Verbose "Computername: $Computername."
       }
     }
      else
     {
      Write-Host "The file '$Inputfile' does not exists. Thus quitting."
      Exit 1
     }
   }

# =============================================================================================================================================
# Read the directory with all the computer names
# =============================================================================================================================================

  if ($Directory -ne "")
   {
    $ComputerNames = @(Get-ChildItem -Path $Directory -Filter *.log | Where {$_.Name -notmatch "powershell"} | Sort-Object Path)
   }

# =============================================================================================================================================
# Read the OU with all the computer names
# =============================================================================================================================================

  if ($OU -ne "")
   {
    Try
     {
      Write-Verbose "Reading the computers from the OU $OU and its sub-OU's." 
      $ComputerNames = @((Get-ADComputer -Filter {Enabled -eq "True"} -SearchScope SubTree -SearchBase $OU -Properties cn).cn)
     }
      Catch
     {
      Write-Host "There is an error: $($_.Exception.Message). Check if '$OU' exists."
      Exit 1
     }
   }

# =============================================================================================================================================
# Count the number of computers. If zero, then quit.
# =============================================================================================================================================

  if($ComputerNames.Count -eq 0)
   {
    Write-Host "There are no computers to process!"
    Exit 2
   }

# =============================================================================================================================================
# Get unique computernames.
# =============================================================================================================================================

  $ComputerNames = $ComputerNames | Get-Unique | Sort-Object

# =============================================================================================================================================
# Define the log file. This log file contains all the results.
# The filenam may contain maximum of 3 computer names. Otherwise the filename may be too long.
# =============================================================================================================================================

  $ComputerNameForFileName = ""
  ForEach ($Computer in $ComputerNames)
   {
    $ComputerNameForFileName = "$ComputerNameForFileName $Computer"
    if($valCounter -ge 3)
     {
      $bolMaxComputers = $true
      Break
     }
    $valCounter++
   }
 
  $ComputerNameForFileName = "SearchInFilesForEntries_" + $(($ComputerNameForFileName.Substring(1)).Replace(" ","_"))

  If($bolMaxComputers)
   {
    $ComputerNameForFileName += " (PLUS $($ComputerNames.Count - $valCounter) MORE)"
   }

  $strLastPartOfFileName = " (" + (Get-Date).ToString('G') + ").csv"
  $strLastPartOfFileName = $strLastPartOfFileName -replace ":","-"
  $strLastPartOfFileName = $strLastPartOfFileName -replace "/","-"

  $strCSVLogFileSucces   = $LogPath + "\"+ $ComputerNameForFileName + $strLastPartOfFileName

# =============================================================================================================================================
# Go through the list of computers
# =============================================================================================================================================

  $valCounter = 1 
  ForEach ($ComputerName in $ComputerNames)
   {
  
    # =============================================================================================================================================
    # Check if the computer can be reached
    # If so, continue.
    # =============================================================================================================================================
      
      $Computername = $ComputerName -replace(".log","")
      Write-Verbose "Processing computer $ComputerName"
      Write-Progress -Activity "Processing the computers" -Status "Processing computer $ComputerName" -PercentComplete ($valCounter / $ComputerNames.Count * 100)
      If(Test-Connection -Quiet -ComputerName $ComputerName -Count 1)
       {
        ForEach ($FileName in $FileNames)
         {
          ForEach ($Entry in $LookFor)
           {
            $UNCFileName   = Convert-ToUNCShare -ComputerName $ComputerName -FilePath $FileName
            if(test-path $UNCFileName)
             { 
              $MatchingFiles = Get-ChildItem -Path $UNCFileName
              ForEach ($MatchingFile in $MatchingFiles)
               {
                
                # =============================================================================================================================================
                # Only process filenames, no directories.
                # =============================================================================================================================================
                
                if((Get-Item $MatchingFile.FullName) -is [system.io.fileinfo])
                 {
                  $Content  = Get-Content -Path $MatchingFile.FullName | Select-String -Pattern $Entry
                  if($Content -ne $NULL)
                   {
                    ForEach ($Line in $Content)
                     { 
                      Write-EntryToResultsFile     `
                         -PCName $ComputerName     `
                         -LocalFileName $(Convert-BackFromUNCShare -ComputerName $ComputerName -UNCFilePath $MatchingFile.FullName)    `
                         -FoundEntry $Entry        `
                         -Line $Line               `
                         -Remark ""
                     }
                   }
                 }
               }
             }
           }
         }
       }
        else
       {
        Write-Verbose "The computer $ComputerName appears to be offline."
       }
      $valCounter++
   }

# =============================================================================================================================================
# Export the CSV file and mail it.
# Exit afterwards.
# =============================================================================================================================================

  Export-ResultsLogFileToCSV

  If($Global:arrTable.Count -gt 0 -and $MailCSVFile)
   {   
     Mail-CSVResultsFile -Sendername "This email address is being protected from spambots. You need JavaScript enabled to view it."                     `
                         -Recipients @("This email address is being protected from spambots. You need JavaScript enabled to view it.","This email address is being protected from spambots. You need JavaScript enabled to view it.") `                                  `
                         -SMTPServer "smtp.office365.com"                     `
                         -SMTPPort 587                                        `
                         -SSL                                                 `                                   `
                         -Attachment $strCSVLogFileSucces                     `
                         -Subject "Overview" 

    Remove-Item -Path $strCSVLogFileSucces -Confirm:$false | Out-Null
   }

  Exit 0

If you want to use the mail options, you will have to modify the last part of the code and specify your own SMTP server details. When used with Office365 you are prompted to enter your Office 365 credentials. Office 365 does not allow anonymous sending of emails. 

 

Attachments:
Download this file (CheckFilesForEntries (v01).7z)CheckFilesForEntries (v01)[CheckFilesForEntries (v01)]4 kB