Public/Add-specToHostsFile.ps1
|
function Add-specToHostsFile { <# .SYNOPSIS Adds one or more specified IP address and hostname mappings to the system's hosts file. .DESCRIPTION This function allows you to add new entries to the system's hosts file. It reads the hosts file once into memory, checks for existing entries (optionally matching on hostname only), and appends any new entries in one write operation to avoid file lock issues. The function supports pipeline input for objects containing `DesiredIP` and `Hostname` properties, making it ideal for bulk operations or use within automation workflows. .PARAMETER DesiredIP The IP address to associate with the hostname. .PARAMETER Hostname The hostname (or domain name) to add to the hosts file. .PARAMETER MatchOnHostnameOnly If specified, the function checks only for the hostname when determining if an entry exists. When this switch is used, existing entries with the same hostname (regardless of IP) will prevent the addition of a new entry. .PARAMETER HostsFilePath Specifies the path to the hosts file. Defaults to the system hosts file location. .INPUTS Accepts pipeline input for objects with `DesiredIP` and `Hostname` properties. .OUTPUTS None .EXAMPLE Add-specToHostsFile -DesiredIP "192.168.1.10" -Hostname "example.com" Adds "192.168.1.10 example.com" to the hosts file if it does not already exist. .EXAMPLE Add-specToHostsFile -DesiredIP "192.168.1.10" -Hostname "example.com" -MatchOnHostnameOnly Adds the entry only if "example.com" is not already present in the hosts file, regardless of IP address. .EXAMPLE $entries = @( [pscustomobject]@{ DesiredIP = "192.168.1.10"; Hostname = "example1.com" }, [pscustomobject]@{ DesiredIP = "192.168.1.20"; Hostname = "example2.com" } ) $entries | Add-specToHostsFile Adds both entries to the hosts file if they do not already exist. .NOTES Author: owen.heaume Version 1.0.0 - Initial release 1.0.1 - Slight refactor to better support unit testing (Create HostFilePath parameter) 1.0.2 - Improved error handling, renaming of function 1.0.3 - Updated to avoid file lock issues during multiple additions. - Writes all new entries in a single operation. #> [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter( Mandatory = $true, ValueFromPipelineByPropertyName = $true )] [string]$DesiredIP, [Parameter( Mandatory = $true, ValueFromPipelineByPropertyName = $true )] [string]$Hostname, [Parameter()] [switch]$MatchOnHostnameOnly, [Parameter()] [string]$HostsFilePath = "$($Env:WinDir)\system32\Drivers\etc\hosts" ) begin { try { # Load the hosts file into memory once $hostsFile = if (Test-Path $HostsFilePath -ea Stop) { Get-Content $HostsFilePath -ea Stop } else { @() } # Collect new entries to add $entriesToAdd = @() } catch { Write-Error "An error occurred loading the hosts file: $_" return } } process { try { $escapedHostname = [Regex]::Escape($Hostname) $patternToMatch = if ($MatchOnHostnameOnly) { ".*\s+$escapedHostname.*" } else { ".*$DesiredIP\s+$escapedHostname.*" } # Check if the entry already exists if ($hostsFile -match $patternToMatch) { Write-Host "$DesiredIP".PadRight(20, ' ') "$Hostname - already exists" -ForegroundColor DarkYellow } else { Write-Host "$DesiredIP".PadRight(20, ' ') "$Hostname - queued for addition" -ForegroundColor Gray $entriesToAdd += "$DesiredIP".PadRight(20, ' ') + $Hostname } } catch { Write-Error "An error occurred processing '$Hostname': $_" } } end { if ($entriesToAdd.Count -eq 0) { Write-Host 'No new entries to add.' -ForegroundColor DarkGray return } try { if ($PSCmdlet.ShouldProcess('hosts file', "Add $($entriesToAdd.Count) new entries")) { # Write all pending entries at once (atomic) Add-Content -Encoding ASCII -Path $HostsFilePath -Value ($entriesToAdd -join "`r`n") -ea Stop Write-Host "Successfully added $($entriesToAdd.Count) entries to hosts file." -ForegroundColor Green } } catch { Write-Error "An error occurred writing to the hosts file: $_" } finally { Write-Host 'Processing complete.' -ForegroundColor Gray } } } |