PowerShell: Function to Add/Remove Local Host Record

Searching through my ‘magic bag of tricks,’ there appears to be this little snippet that I’ve used on a number of occasions. Developers often test their web app changes by temporarily adding DNS entries onto certain hosts prior to editing the domain’s DNS or the public DNS zones. Hence one would find two functions that would serve this purpose: addLocalhostRecord and removeLocalhostRecord. I hope this would be useful to Google searchers. Enjoy.

function confirmation($content,$testValue="I confirm",$maxAttempts=3){
    $confirmed=$false;
    $attempts=0;        
    $content|write-host
    write-host "Please review this content for accuracy.`r`n"
    while ($attempts -le $maxAttempts){
        if($attempts++ -ge $maxAttempts){
            write-host "A maximum number of attempts have reached. No confirmations received!`r`n"
            break;
            }
        $userInput = Read-Host -Prompt "Please type in this value => $testValue <= to confirm";
        if ($userInput.ToLower() -ne $testValue.ToLower()){
            cls;
            $content|write-host
            write-host "Attempt number $attempts of $maxAttempts`: $userInput does not match $testValue. Try again..`r`n"
            }else{
                $confirmed=$true;
                write-host "Confirmed!`r`n";
                break;
                }
        }
    return $confirmed;
}

function addLocalhostRecord{    
    param(    
        $record,
        $insertAfter,
        $servers
        )
    $hostFiles=$($servers|%{"\\$_\C$\windows\system32\drivers\etc\hosts"})
    function addLocalHostRecord{
        param(    
        $record,
        $hostfile="c:\windows\system32\drivers\etc\hosts",
        $searchString=$false
        )
     
        function insertAfterMatchString{
            param(
                $content,
                $searchString,
                $insert
            )
            [int]$matchedIndex=.{$lines=[array]$content
                                    for ($i=0;$i -lt $lines.Count;$i++) {if($lines[$i] -like "$searchString*"){return $i}}
                                    }
            if($matchedIndex){
                $nextAvailableSpaceIndex=.{for($i=$matchedIndex;$i -lt $content.Length;$i++){
                                                $isEmpty=$content[$i] -match "^\s*$"
                                                #$isEmpty=$content[$i].Trim().isEmpty()
                                                if ($isEmpty){return $i}
                                                }
                                            }
                $content=$content[0..$($nextAvailableSpaceIndex-1)]+$insert+$content[$($nextAvailableSpaceIndex)..$($content.length-1)]
                write-host "$insert will be inserted after $($content[$nextAvailableSpaceIndex-1])`r`n-----------------------`r`n"
            }else{
                $content+=$insert
                write-host "$searchString has not matched anything.";
            }
            return $content
        }
     
        $validPath=[System.IO.File]::Exists($hostfile) # Slightly faster than Test-Path for true positives but not true negatives
            if($validPath){
            $fileContent=Get-Content -Path $hostfile
            $url=.{[void]($record -match "([0-9A-Za-z_\.\-]*)$");$matches[1]}
            $entryExists=$fileContent -match $url
            if(!$entryExists){
                if($searchString){
                    $fileContent=insertAfterMatchString -content $fileContent -searchString $searchString -insert $record
                    }else{
                        $fileContent+=$record
                        }         
                $confirmed=confirmation -content $fileContent
                if ($confirmed){
                    $backupFile=$hostFile+"_backup"
                    try{
                        Rename-Item -Path $hostfile -NewName $backupFile -ErrorAction Stop
                        }
                        catch{
                            write-host "Unable to rename. Now trying to delete previous backup and retry"
                            Remove-item $backupFile -Force
                            Rename-Item -Path $hostfile -NewName $backupFile
                            }
                    # The try-catch method overcomes this error
                    #Rename-Item : Cannot create a file when that file already exists.
                    #At line:1 char:1
                    #+ Rename-Item -Path $hostfile -NewName $($hostFile+"_backup") -Force
                    #+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                    #    + CategoryInfo          : WriteError: (C:\windows\system32\drivers\etc\hosts:String) [Rename-Item], IOException
                    #    + FullyQualifiedErrorId : RenameItemIOError,Microsoft.PowerShell.Commands.RenameItemCommand
                    $fileContent|set-Content $hostfile
                    write-host "$record has been added to $hostfile successfully."
                    return $true
                    }else{
                        write-host "No changes were made as there were no confirmations."
                        return $false;
                        }
                }
            else{
                write-host "$hostRecord.$domain already exists in $hostFile. Nothing were added."
                return $false;
                }
        }else{
            write-host "Unable to access $hostfile. Action aborted.";
            return $false;
            }
    }
     
    foreach ($hostfile in $hostFiles){
        write-host "Processing $hostfile."
        pause;        
        addLocalHostRecord -record $record -hostfile $hostFile -searchString $insertAfter
        }
}

# addLocalHostRecord -record "192.500.500.500`tyamama.kimconnect.com" -insertAfter "yadada.kimconnect.com" -servers $labShervers

function removeLocalhostRecord{
    param(    
        $record,
        $servers
        )
    $hostFiles=$($servers|%{"\\$_\C$\windows\system32\drivers\etc\hosts"})

    foreach ($hostfile in $hostFiles){
        write-host "Processing $hostfile."
        $validPath=[System.IO.File]::Exists($hostfile)
        if(!$validPath){
            write-host "Couldn't access $hostfile. Skipping this record...`r`n";
            continue;
            }
        pause;
        $content=Get-Content -Path $hostfile       
        [int]$matchedIndex = [array]::indexof($content, $content -match $record)
        if($matchedIndex -gt 0){
            $content=$content[0..$($matchedIndex-1)]+$content[$($matchedIndex+1)..$($content.length-1)]
            write-host "$record has been found at index $matchedIndex`r`n-----------------------`r`n"
            $confirmed=confirmation -content $content
            if ($confirmed){
                $backupFile=$hostFile+"_backup"
                try{
                    Rename-Item -Path $hostfile -NewName $backupFile -ErrorAction Stop
                    }
                    catch{
                        write-host "Unable to rename. Now trying to delete previous backup and retry process"
                        Remove-item $backupFile -Force
                        Rename-Item -Path $hostfile -NewName $backupFile
                        }               
                $content|set-Content $hostfile
                write-host "$record has been removed from $hostfile successfully."
                }else{
                    write-host "No changes were made to $hostfile as there were no confirmations."
                    }            
            }else{
                write-host "$record has not matched any line inside $hostfile";
            }
        }
}

# removeLocalHostRecord "preying-mantis.kimconnect.com" $labServers

Leave a Reply

Your email address will not be published. Required fields are marked *