Public/Remove-specFromHostsFile.ps1

function Remove-specFromHostsFile {
    <#
    .SYNOPSIS
    Removes one or more hostnames (and associated IPs) from the system's hosts file.
 
    .DESCRIPTION
    This function removes all entries that contain the specified hostnames from the hosts file.
    It supports removing multiple hostnames at once and preserves comments and formatting.
 
    .PARAMETER Hostname
    One or more hostnames (or domain names) to remove from the hosts file.
 
    .PARAMETER HostsFilePath
    Specifies the path to the hosts file. Defaults to the system hosts file.
 
    .INPUTS
    [string[]] – You can pipe hostnames or pass them directly.
 
    .OUTPUTS
    None
 
    .EXAMPLE
    Remove-specFromHostsFile -Hostname "example.com","example2.com"
 
    .EXAMPLE
    "example.com","example2.com" | Remove-specFromHostsFile
 
    .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
            1.0.3 - Added support for multiple hostnames and improved safety.
    #>


    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [Parameter(
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [string[]]$Hostname,

        [Parameter()]
        [string]$HostsFilePath = "$($Env:WinDir)\system32\Drivers\etc\hosts"
    )

    begin {
        try {
            if (-not (Test-Path $HostsFilePath)) {
                throw "Hosts file not found at path: $HostsFilePath"
            }

            # Read once — preserving all lines (comments, blanks, etc.)
            $hostsFile = Get-Content -Path $HostsFilePath -ErrorAction Stop
        } catch {
            Write-Error "Error reading hosts file: $_"
            return
        }
    }

    process {
        foreach ($name in $Hostname) {
            try {
                Write-Host "Checking for '$name' in hosts file..." -ForegroundColor Gray
                $escaped = [Regex]::Escape($name)
                $pattern = "\b$escaped\b"

                $matchesFound = $hostsFile | Where-Object { $_ -match $pattern }

                if ($matchesFound) {
                    if ($PSCmdlet.ShouldProcess($name, 'Remove from hosts file')) {
                        Write-Host "$name - removing..." -ForegroundColor Yellow
                        # Remove matching lines but keep comments, whitespace, and unrelated entries
                        $hostsFile = $hostsFile | Where-Object {
                            ($_ -notmatch $pattern) -or ($_ -match '^\s*#')
                        }
                    }
                } else {
                    Write-Host "$name - not found in hosts file" -ForegroundColor DarkYellow
                }
            } catch {
                Write-Error "An error occurred processing '$name': $_"
            }
        }
    }

    end {
        try {
            if ($PSCmdlet.ShouldProcess($HostsFilePath, 'Write updated hosts file')) {
                # Write safely
                $hostsFile | Out-File -FilePath $HostsFilePath -Encoding ASCII -Force
                Write-Host 'Hosts file updated successfully.' -ForegroundColor Green
            }
        } catch {
            Write-Error "Error writing updated hosts file: $_"
        }
    }
}