Updated on 2024-10-21 GMT+08:00

Configuring the update_hosts_win.ps1 Script

Modify the configuration in the example script to meet you specific requirements.

Prerequisites

You have completed all preparations.

Procedure

  1. Create a file named update_hosts_lwin.ps1 on the server where the script is executed, and copy the following script content to the file.

    # Configuration
    # Path to the CSV file with server information. Must exist before running the script.
    $csvFile = "C:\Users\Public\target_servers.csv"  # Manually configure
     
    # Path to the hosts content file. Must exist before running the script.
    $hostsFile = "C:\Users\Public\hosts_content.txt"  # Manually configure
     
    # Directory for storing log files. Will be created if it doesn't exist.
    $logDir = "C:\Users\Public\Hosts_Script_Logs"  # Automatically created
     
    # Log file for general run information.
    $runLog = Join-Path $logDir "run.log"  # Automatically created
     
    # Log file for error messages.
    $errorLog = Join-Path $logDir "error.log"  # Automatically created
     
    # Log file for summary information.
    $summaryLog = Join-Path $logDir "summary.log"  # Automatically created
     
     
    # Initialize log directory and files
    function Initialize-Logs {
        if (-not (Test-Path $logDir)) {
            New-Item -Path $logDir -ItemType Directory
        }
        Add-Content -Path $runLog -Value "========================================"
        Add-Content -Path $runLog -Value "[INFO] $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - Starting new update execution"
        Add-Content -Path $runLog -Value "========================================"
     
        Add-Content -Path $errorLog -Value "========================================"
        Add-Content -Path $errorLog -Value "[INFO] $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - Starting new update execution"
        Add-Content -Path $errorLog -Value "========================================"
     
        Add-Content -Path $summaryLog -Value "========================================"
        Add-Content -Path $summaryLog -Value "[INFO] $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - Starting new update execution"
        Add-Content -Path $summaryLog -Value "========================================"
    }
     
    # Log info function
    function Log-Info {
        param (
            [string]$message
        )
        $logMessage = "[INFO] $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - $message"
        Add-Content -Path $runLog -Value $logMessage
        Write-Output $logMessage
    }
     
    # Log error function
    function Log-Error {
        param (
            [string]$message
        )
        $logMessage = "[ERROR] $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - $message"
        Add-Content -Path $runLog -Value $logMessage
        Add-Content -Path $errorLog -Value $logMessage
        Write-Output $logMessage
    }
     
    # Read server information from CSV file
    function Read-ServersFromCSV {
        param (
            [string]$csvFile
        )
        if (-not (Test-Path $csvFile)) {
            Log-Error "CSV file '$csvFile' not found."
            exit 1
        }
        return Import-Csv -Path $csvFile
    }
     
    # Read hosts content from TXT file
    function Read-HostsContentFromTXT {
        param (
            [string]$hostsFile
        )
        if (-not (Test-Path $hostsFile)) {
            Log-Error "Hosts content file '$hostsFile' not found."
            exit 1
        }
        return Get-Content -Path $hostsFile -Raw
    }
     
    # Add to TrustedHosts
    function Add-ToTrustedHosts {
        param (
            [string]$ip
        )
        # Check current TrustedHosts list
        $trustedHostsPath = "WSMan:\localhost\Client\TrustedHosts"
        $trustedHosts = (Get-Item $trustedHostsPath).Value
     
        if ($trustedHosts -eq $null -or $trustedHosts -eq "") {
            # Set the initial trusted host
            Set-Item $trustedHostsPath -Value $ip -Force
            Log-Info "Set initial TrustedHosts value to $ip"
        } elseif ($trustedHosts -notlike "*$ip*") {
            # Add new IP to TrustedHosts if not already present
            $updatedTrustedHosts = if ($trustedHosts -eq "*") { $ip } else { "$trustedHosts,$ip" }
            try {
                Set-Item $trustedHostsPath -Value $updatedTrustedHosts -Force
                Log-Info "Added $ip to TrustedHosts"
            } catch {
                Log-Error "Failed to add $ip to TrustedHosts: $_"
            }
        } else {
            Write-Host "TrustedHosts list already contains IP $ip."
        }
    }
     
    # Initialize log files
    Initialize-Logs
     
    # Verify CSV file
    if (-not (Test-Path $csvFile)) {
        Log-Error "CSV file '$csvFile' not found."
        exit 1
    }
     
    # Verify hosts file
    if (-not (Test-Path $hostsFile)) {
        Log-Error "Hosts content file '$hostsFile' not found."
        exit 1
    }
     
    # Read server information from CSV file
    $servers = Read-ServersFromCSV -csvFile $csvFile
     
    # Read hosts content from TXT file
    $hostsContent = Read-HostsContentFromTXT -hostsFile $hostsFile
     
    # Counters for success and failure
    $successCount = 0
    $failureCount = 0
    $failedServers = @()
     
    # Remote script block
    $remoteScriptBlock = {
        param (
            [string]$hostsContent
        )
        $hostsFilePath = 'C:\Windows\System32\drivers\etc\hosts'
     
        # Read the file content
        $content = Get-Content -Path $hostsFilePath
     
        # Initialize flag
        $inBlock = $false
        $newContent = @()
     
        # Traverse file content
        foreach ($line in $content) {
            if ($line -match '#Migration-proxy-start') {
                $inBlock = $true
            }
            if (-not $inBlock) {
                $newContent += $line
            }
            if ($line -match '#Migration-proxy-end') {
                $inBlock = $false
                continue
            }
        }
     
        # Remove trailing empty lines
        while ($newContent[-1] -eq '') {
            $newContent = $newContent[0..($newContent.Count - 2)]
        }
     
        # Write the new content back to the file
        $newContent | Set-Content -Path $hostsFilePath
     
        # Append new Migration-proxy section
        Add-Content -Path $hostsFilePath -Value $hostsContent
     
        Write-Output 'Successfully updated hosts file on remote server.'
    }
     
    # Main script logic
    Log-Info "Script execution started."
     
    foreach ($server in $servers) {
        $username = $server.username
        $ip = $server.ip
        $password = $server.password
     
        if (-not $username -or -not ${ip} -or -not $password) {
            Log-Error "Invalid server entry: $username, ${ip}, $password. Skipping."
            continue
        }
     
        Log-Info "Starting update for $username@${ip}"
     
        $securePassword = ConvertTo-SecureString $password -AsPlainText -Force
        $credential = New-Object System.Management.Automation.PSCredential ($username, $securePassword)
     
        Add-ToTrustedHosts -ip $ip
     
        try {
            $session = New-PSSession -ComputerName ${ip} -Credential $credential -ErrorAction Stop
     
            Invoke-Command -Session $session -ScriptBlock $remoteScriptBlock -ArgumentList $hostsContent
     
            Remove-PSSession -Session $session
            Log-Info "Updated hosts on ${ip} successfully"
            $successCount++
        }
        catch {
            Log-Error "Failed to update hosts on ${ip}: $_"
            $failedServers += "$username@${ip}"
            $failureCount++
        }
    }
     
    # Calculate failure and success percentages
    $totalCount = $servers.Count
    if ($totalCount -gt 0) {
        $failurePercentage = [math]::Round(($failureCount / $totalCount) * 100, 2)
        $successPercentage = [math]::Round(($successCount / $totalCount) * 100, 2)
    } else {
        $failurePercentage = 0
        $successPercentage = 100
    }
     
    # Output summary result and log to file
    $summaryContent = @"
    ========================================
    [SUMMARY] $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - Execution Update Summary
    ========================================
    Total number of servers: $totalCount
    Number of successful updates: $successCount
    Number of failed updates: $failureCount
    Success rate: $successPercentage%
    Failure rate: $failurePercentage%
    ----------------------------------------
    "@
     
    if ($failedServers.Count -gt 0) {
        $summaryContent += "Failed servers:`n"
        foreach ($server in $failedServers) {
            $summaryContent += "  - $server`n"
        }
    }
    $summaryContent += "========================================"
     
    # Output summary result to log file and terminal
    $summaryContent | Add-Content -Path $summaryLog
    Write-Output $summaryContent
    Log-Info "Script execution completed. Check $summaryLog for summary."

  2. Modify the following parameters in the script to meet your needs:

    • $logDir = "C:\Users\Public\Hosts_Script_Logs"
      • Description: log directory, which is used to store run, error, and summary logs.
      • Default value: C:\Users\Public\Hosts_Script_Logs
      • Suggestion: Change the value to a directory for which the current user has the write permission.
      • Example: $logDir ="C:\Users\username\Documents\Hosts_Script_Logs"
    • $csvFile = "C:\Users\Public\target_servers.csv"
      • Description: CSV file path. The file contains the source server information.
      • Default value: C:\Users\Public\target_servers.csv
      • Suggestion: Use an absolute path or a correct relative path. If the CSV file path changes, you need to update the path here.
      • Example: $csvFile = "C:\Users\username\Documents\servers.csv"
    • $hostsFile = "C:\Users\Public\hosts_content.txt"
      • Description: hosts file path. The file contains the content to be added to the hosts file on the source servers.
      • Default value: C:\Users\Public\hosts_content.txt
      • Suggestion: Use an absolute path or a correct relative path.
      • Example: $hostsFile = "C:\Users\username\Documents\hosts_content.txt"

  3. After the configuration items are modified and saved, run PowerShell as administrator and execute the script.

    .\update_hosts_win.ps1

    The script will output log information in the terminal window and generate a result report upon completion. You can view this report in the summary.log file in the directory specified by $logDir.