Last active
October 9, 2021 16:11
-
-
Save joshooaj/3539a54f58d0f7bf1cd5a4e27aab4908 to your computer and use it in GitHub Desktop.
My hosts file display and manipulation functions for my profile script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class HostsFileEntry { | |
[IPAddress] $IP | |
[string[]] $Hosts | |
[string] ToString() { | |
return "$($this.IP) $([string]::Join(' ', $this.Hosts))" | |
} | |
} | |
function Get-HostsFileEntry { | |
<# | |
.SYNOPSIS | |
Gets a list of all hosts file entries | |
.DESCRIPTION | |
Gets a list of all hosts file entries and returns them as [HostsFileEntry] objects. | |
.PARAMETER IP | |
Not used yet | |
.PARAMETER Hosts | |
Not used yet | |
.EXAMPLE | |
Get-HostsFileEntry | Foreach-Object { $_.ToString() } | |
Prints all hosts file entries in a similar way to how they're entered in the hosts file. | |
#> | |
[CmdletBinding(DefaultParameterSetName = 'All')] | |
[OutputType([HostsFileEntry])] | |
param ( | |
[Parameter(ParameterSetName = 'ByIP')] | |
[IPAddress[]] | |
$IP, | |
[Parameter(ParameterSetName = 'ByHost')] | |
[string[]] | |
$Hosts | |
) | |
process { | |
$hostsPath = Join-Path -Path ([environment]::GetFolderPath([environment+specialfolder]::System)) -ChildPath 'drivers\etc\hosts' | |
Get-Content -Path $hostsPath | Where-Object { ![string]::IsNullOrWhiteSpace($_) -and !$_.Trim().StartsWith('#')} | Foreach-Object { | |
$entry = @{} | |
$entry.IP, $entry.Hosts = $_.Split(@(' ', "`t"), [System.StringSplitOptions]::RemoveEmptyEntries) | |
$obj = [HostsFileEntry]$entry | |
switch ($PSCmdlet.ParameterSetName) { | |
'ByIP' { | |
if ($entry.IP -eq $IP) { | |
Write-Output $obj | |
} | |
} | |
'ByHost' { | |
foreach ($h in $Hosts) { | |
if ($h -in $entry.Hosts) { | |
Write-Output $obj | |
break | |
} | |
} | |
} | |
default { | |
Write-Output $obj | |
} | |
} | |
} | |
} | |
} | |
function New-HostsFileEntry { | |
[CmdletBinding()] | |
[OutputType([HostsFileEntry])] | |
param ( | |
[Parameter(Mandatory)] | |
[ValidateScript({ | |
$supportedAddressFamilies = @( | |
[System.Net.Sockets.AddressFamily]::InterNetwork, | |
[System.Net.Sockets.AddressFamily]::InterNetworkV6 | |
) | |
if ($_.AddressFamily -notin $supportedAddressFamilies) { | |
throw "IPAddress must be IPv4 or IPv6." | |
} | |
return $true | |
})] | |
[IPAddress] | |
$IP, | |
[Parameter(Mandatory)] | |
[ValidateCount(1, 100)] | |
[ValidateNotNullOrEmpty()] | |
[string[]] | |
$Hosts | |
) | |
process { | |
Write-Output ([HostsFileEntry]@{IP = $IP; Hosts = $Hosts}) | |
} | |
} | |
function New-HostsFileContent { | |
[CmdletBinding()] | |
[OutputType([string])] | |
param( | |
[Parameter(ValueFromPipeline)] | |
[HostsFileEntry[]] | |
$InputObject | |
) | |
begin { | |
$header = @" | |
# Copyright (c) 1993-2009 Microsoft Corp. | |
# | |
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows. | |
# | |
# This file contains the mappings of IP addresses to host names. Each | |
# entry should be kept on an individual line. The IP address should | |
# be placed in the first column followed by the corresponding host name. | |
# The IP address and the host name should be separated by at least one | |
# space. | |
# | |
# Additionally, comments (such as these) may be inserted on individual | |
# lines or following the machine name denoted by a '#' symbol. | |
# | |
# For example: | |
# | |
# 102.54.94.97 rhino.acme.com # source server | |
# 38.25.63.10 x.acme.com # x client host | |
# localhost name resolution is handled within DNS itself. | |
# 127.0.0.1 localhost | |
# ::1 localhost | |
"@ | |
$builder = [text.stringbuilder]::new($header) | |
} | |
process { | |
foreach ($entry in $InputObject) { | |
$null = $builder.AppendLine($entry.ToString()) | |
} | |
} | |
end { | |
Write-Output $builder.ToString() | |
} | |
} | |
function Add-HostsFileEntry { | |
<# | |
.SYNOPSIS | |
Adds one or more hosts file entries to the hosts file. Requires admin. | |
.DESCRIPTION | |
Adds one or more hosts file entries to the hosts file. Note that the hosts file will be | |
rewritten completely as this command will read all existing entries into memory, add the new | |
entry, and generate a new hosts file with the old and new rows. Any custom comments or | |
formatting will be overwritten. | |
.PARAMETER InputObject | |
Specifies one or more HostsFileEntry objects from New-HostsFileEntry. | |
.PARAMETER IP | |
Specifies the IP address, which is the first value in each row of a hosts file. | |
.PARAMETER Hosts | |
Specifies one or more hostnames or fully qualified domain names associated with the IP address. | |
.PARAMETER Append | |
Specifies that if an existing entry is found matching the IP, the hosts values provided should | |
be appended to any existing values. Note that duplicate values will be discarded. | |
.PARAMETER Force | |
If an existing entry is found matching the IP address, it can only be updated when specifying | |
the Force parameter switch. | |
.EXAMPLE | |
Add-HostsFileEntry -IP 192.168.1.100 -Hosts mylaptop, mylaptop.domain.com -Force | |
Specifies that a hosts file entry like "192.168.1.100 mylaptop mylaptop.domain.com" should be | |
created if it doesn't exist, and if it does exist, the existing entry should be overwritten. | |
#> | |
[CmdletBinding()] | |
param ( | |
[Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'FromObject')] | |
[Alias('HostsFileEntry')] | |
[HostsFileEntry] | |
$InputObject, | |
[Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'FromValues')] | |
[ValidateScript({ | |
$supportedAddressFamilies = @( | |
[System.Net.Sockets.AddressFamily]::InterNetwork, | |
[System.Net.Sockets.AddressFamily]::InterNetworkV6 | |
) | |
if ($_.AddressFamily -notin $supportedAddressFamilies) { | |
throw "IPAddress must be IPv4 or IPv6." | |
} | |
return $true | |
})] | |
[IPAddress] | |
$IP, | |
[Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'FromValues')] | |
[ValidateCount(1, 100)] | |
[ValidateNotNullOrEmpty()] | |
[string[]] | |
$Hosts, | |
[Parameter()] | |
[switch] | |
$Append, | |
[Parameter()] | |
[switch] | |
$Force | |
) | |
process { | |
if ($PSCmdlet.ParameterSetName -eq 'FromValues') { | |
$InputObject = New-HostsFileEntry -IP $IP -Hosts $Hosts | |
} | |
$entries = @(Get-HostsFileEntry) | |
$existingEntry = $entries | Where-Object { $_.IP -eq $InputObject.IP } | |
if ($null -ne $existingEntry) { | |
if (-not $Force) { | |
Write-Error "There's already a hosts file entry for IP $($InputObject.IP). Use -Force to overwrite existing entries, and add -Append to add new hosts to existing host entries for the IP address." | |
return | |
} | |
if ($Append) { | |
$existingEntry.Hosts = $InputObject.Hosts + ( $existingEntry.Hosts | Where-Object { $_ -notin $InputObject.Hosts } ) | |
} | |
else { | |
$existingEntry.Hosts = $InputObject.Hosts | |
} | |
} | |
else { | |
$entries += $InputObject | |
} | |
$newHostsContent = $entries | New-HostsFileContent | |
$hostsPath = Join-Path -Path ([environment]::GetFolderPath([environment+specialfolder]::System)) -ChildPath 'drivers\etc\hosts' | |
[io.file]::WriteAllText($hostsPath, $newHostsContent) | |
} | |
} | |
function Remove-HostsFileEntry { | |
<# | |
.SYNOPSIS | |
Removes a hosts file entry by IP address. Requires admin. | |
.DESCRIPTION | |
Removes a hosts file entry by IP address. Requires admin. | |
.PARAMETER IP | |
Specifies an IPv4 or IPv6 IP address to remove if it exists in the hosts file. | |
.EXAMPLE | |
Remove-HostsFileEntry -IP 192.168.1.100 | |
Removes the hosts file entry for IP 192.168.1.100 or returns an error if the entry does not | |
exist. | |
#> | |
[CmdletBinding()] | |
param ( | |
[Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'FromValues')] | |
[ValidateScript({ | |
$supportedAddressFamilies = @( | |
[System.Net.Sockets.AddressFamily]::InterNetwork, | |
[System.Net.Sockets.AddressFamily]::InterNetworkV6 | |
) | |
if ($_.AddressFamily -notin $supportedAddressFamilies) { | |
throw "IPAddress must be IPv4 or IPv6." | |
} | |
return $true | |
})] | |
[IPAddress] | |
$IP | |
) | |
process { | |
$entries = Get-HostsFileEntry | |
$dirty = $false | |
for ($i = 0; $i -lt $entries.Count; $i++) { | |
if ($entries[$i].IP -eq $IP) { | |
$entries = $entries | Where-Object { $_.IP -ne $IP} | |
$dirty = $true | |
break | |
} | |
} | |
if ($dirty) { | |
$newHostsContent = $entries | New-HostsFileContent | |
$hostsPath = Join-Path -Path ([environment]::GetFolderPath([environment+specialfolder]::System)) -ChildPath 'drivers\etc\hosts' | |
[io.file]::WriteAllText($hostsPath, $newHostsContent) | |
} | |
else { | |
Write-Error "No hosts file entry found for IP $($IP.ToString())" | |
} | |
} | |
} | |
$hostsEntries = Get-HostsFileEntry | |
if ($hostsEntries.Count -gt 0) { | |
Write-Host -Object "You have $($hostsEntries.Count) hosts file entries" -ForegroundColor Green | |
$hostsEntries | Foreach-Object { Write-Host -Object " - $_" -ForegroundColor Green } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment