Moving Day for Your Website: How to Make Your Plesk Migration Smooth as Silk!

So, you’re gearing up to migrate your Plesk-hosted websites to a shiny new server? Whether it’s for better performance, more resources, or just a fresh start, “moving day” for your digital home can feel a bit daunting. One of the biggest headaches? Making sure your website and email don’t go missing in action during the transition.

That’s where the magic of a good DNS audit comes in!

Think of DNS (Domain Name System) as the internet’s phone book. When someone types in your website address, DNS is what tells their computer exactly where to find your site and its emails. During a server migration, you’re essentially changing your website’s address in that phone book. If you get it wrong, visitors won’t find you!

We’ve cooked up a super handy (and kind of clever, if we do say so ourselves!) PowerShell script to take the guesswork out of this crucial step. It dives deep into your current Plesk server running on Windows operating system, checking every single domain you host to see exactly where it’s pointing.

What Does This Script Tell You About Your Domains?

This isn’t just a simple “yes or no” answer. Our script gives you a detailed breakdown of each domain’s “resolution status,” helping you plan your migration with precision:

  • “Lost and Found” – Not Resolved: If a domain isn’t resolving at all, it’s like a phone number that’s been disconnected. Maybe it’s an old, unused domain, or perhaps there’s a missing DNS record. This is a great opportunity to do some digital decluttering and remove domains you no longer need.
  • “Right at Home” – Resolved to Current Server IP: These are your active, happy campers! Your website and/or email for these domains are currently hosted on your existing Plesk server. These are the ones you’ll definitely need to update in the DNS phone book to point to your new server. This is the bulk of your migration work.
  • “Flown the Coop” – Resolved to an External Host: Sometimes, a domain might still be listed in your Plesk, but the actual website or email services have moved elsewhere. Our script catches these, letting you know that these domains are no longer using your current server and can likely be safely removed from Plesk, simplifying your migration.
  • “Independent Spirit” – External Name Servers, but Still Pointing Here: This is an interesting one! Some users might manage their domain’s name servers (the primary pointers) through an external provider (like GoDaddy or Cloudflare), but their website still resolves to your Plesk server. For these clients, you’ll need to gently remind them that they’ll need to personally update their DNS records to point to your new server’s IP address. It’s a team effort!
# Set the path to the Plesk bin directory
# Adjust this path if your Plesk installation is in a different location
$pleskBinPath = "C:\Program Files (x86)\Plesk\bin"

# Set the public DNS server to use for DNS lookups (for A records, NS records, and MX records)
$publicDnsServer = "8.8.8.8" # wtad1. You can also use "1.1.1.1" (Cloudflare) or another reliable one.

# --- Get all relevant Plesk server IP addresses ---
try {
    $serverIps = (Get-NetIPAddress | Where-Object {
        $_.AddressFamily -eq 'IPv4' -and
        $_.PrefixLength -lt 32 -and # Exclude loopback/link-local IPv4 addresses
        $_.IPAddress -notlike "169.254.*" -and # Exclude APIPA addresses
        $_.IPAddress -notlike "127.0.0.1"
    } | Select-Object -ExpandProperty IPAddress | Sort-Object -Unique)
    if ($serverIps.Count -eq 0) {
        Write-Warning "Could not determine any relevant IPv4 addresses for the server automatically. Please set them manually if necessary."
        # **IMPORTANT: Manually add your server's IPs here if auto-detection fails, e.g.:**
        # $serverIps = @("192.168.1.10", "192.168.1.11")
    }
} catch {
    Write-Warning "An error occurred trying to detect server IPs: $($_.Exception.Message)"
    # **IMPORTANT: Manually add your server's IPs here if auto-detection fails, e.g.:**
    # $serverIps = @("192.168.1.10", "192.168.1.11")
}
Write-Host "Plesk Server's IP Addresses (for comparison): $($serverIps -join ', ')"

# Array to store results for CSV export
$resultsForCsv = @()

Write-Host "Getting list of domains from Plesk..."
# Construct the command to list domains
$listDomainsCommand = @(
    "$pleskBinPath\domain",
    "--list"
)

# Execute the Plesk command to get all domain names
$domainsOutput = Invoke-Command -ScriptBlock { & $args[0] $args[1] } -ArgumentList $listDomainsCommand
$domains = $domainsOutput | Where-Object { $_ -ne "" } | ForEach-Object { $_.Trim() } # Filter and trim

if ($domains.Count -eq 0) {
    Write-Warning "No domains found in Plesk or unable to retrieve domain list. Exiting."
    exit # Exit the script if no domains are found
}

Write-Host "Checking DNS resolution, Nameservers, and MX records for each domain..."
foreach ($domain in $domains) {
    Write-Host "  Checking: $domain"
    # --- Initialize variables for this domain's info ---
    $domainStatus = "OK"
    $statusDetails = ""
    $resolvedA_Ips = @()
    $nameServers = @()
    $mxRecords = @() # Raw MX record objects with NameExchange and Preference
    $mxIpAddressesReport = @() # Formatted string for MX IP addresses for reporting

    # --- Get Nameservers (NS records) ---
    try {
        # Filter for actual NS records. Sometimes Resolve-DnsName returns A/AAAA for the domain itself.
        $nsRecords = Resolve-DnsName -Name $domain -Type NS -Server $publicDnsServer -ErrorAction SilentlyContinue -ErrorVariable NSErrors | Where-Object {$_.Type -eq "NS"}
        if ($nsRecords) {
            $nameServers = $nsRecords | Select-Object -ExpandProperty NameHost | Sort-Object -Unique
        } else {
            if ($NSErrors) {
                $nameServers = @("N/A (Error: $($NSErrors[0].Exception.Message))")
            } else {
                $nameServers = @("N/A (No NS records found)")
            }
        }
    } catch {
        $nameServers = @("N/A (Error retrieving NS: $($_.Exception.Message))")
    }
    $nsString = ($nameServers -join ', ')
    Write-Host "    Nameservers: $nsString"

    # --- Check A record resolution ---
    try {
        $aRecords = Resolve-DnsName -Name $domain -Type A -Server $publicDnsServer -ErrorAction Stop -ErrorVariable DnsError
        $resolvedA_Ips = $aRecords | Select-Object -ExpandProperty IPAddress
        if ($resolvedA_Ips.Count -eq 0) {
            $domainStatus = "No A record found"
            $statusDetails = "Resolved, but no A record found."
        } else {
            $matchedServerIp = $false
            foreach ($resolvedIp in $resolvedA_Ips) {
                if ($serverIps -contains $resolvedIp) {
                    $matchedServerIp = $true
                    break
                }
            }
            if (-not $matchedServerIp) {
                $domainStatus = "Wrong A Record IP"
                $statusDetails = "Resolves to [$($resolvedA_Ips -join ', ')], expected one of: $($serverIps -join ', ')."
            }
        }
    } catch {
        $domainStatus = "Unresolvable"
        $statusDetails = "NOT resolvable at all! Error: $($_.Exception.Message)."
    }

    # --- Check MX record resolution ---
    try {
        $mxRecordsQueryResult = Resolve-DnsName -Name $domain -Type MX -Server $publicDnsServer -ErrorAction SilentlyContinue -ErrorVariable MXErrors
        if ($mxRecordsQueryResult) {
            $mxRecords = @($mxRecordsQueryResult | ForEach-Object {
                [PSCustomObject]@{
                    NameExchange = $_.NameExchange
                    Preference = $_.Preference
                }
            } | Sort-Object Preference)

            $mxPointsToPlesk = $false
            foreach ($mxRecord in $mxRecords) {
                if ([string]::IsNullOrEmpty($mxRecord.NameExchange)) {
                    $mxIpAddressesReport += "Invalid MX Record (No NameExchange) -> Could not resolve IP (Empty MX hostname)"
                    continue
                }

                try {
                    $mxResolvedIps = (Resolve-DnsName -Name $mxRecord.NameExchange -Type A -Server $publicDnsServer -ErrorAction Stop).IPAddress
                    $mxIpAddressesReport += "$($mxRecord.NameExchange) ($($mxRecord.Preference)) -> $($mxResolvedIps -join ', ')"

                    # Check if this specific MX record's resolved IP points to Plesk
                    foreach ($mxHostIp in $mxResolvedIps) {
                        if ($serverIps -contains $mxHostIp) {
                            $mxPointsToPlesk = $true
                            break # Found a matching IP for at least one MX host
                        }
                    }
                } catch {
                    $mxIpAddressesReport += "$($mxRecord.NameExchange) ($($mxRecord.Preference)) -> Could not resolve IP ($($_.Exception.Message))"
                }
            }

            if (-not $mxPointsToPlesk) {
                if ($domainStatus -eq "OK") {
                    $domainStatus = "MX Not Pointing to Plesk"
                    $statusDetails = "MX records do not resolve to any of the Plesk server IPs."
                }
                $statusDetails += "`n    MX Records: $($mxIpAddressesReport -join '; '). MX does not point to Plesk."
            } else {
                Write-Host "    MX Records: $($mxIpAddressesReport -join '; '). (Points to Plesk)"
            }
        } else {
            if ($MXErrors) {
                Write-Host "    MX Records: N/A (Error: $($MXErrors[0].Exception.Message))"
                if ($domainStatus -eq "OK") {
                    $domainStatus = "MX Unresolvable"
                    $statusDetails = "MX records could not be resolved. Error: $($MXErrors[0].Exception.Message)."
                } else {
                    $statusDetails += "`n    MX Records: N/A (Error: $($MXErrors[0].Exception.Message))."
                }
            } else {
                Write-Host "    MX Records: N/A (No MX records found)"
                # Decide if you want to flag this as a problem
                # if ($domainStatus -eq "OK") { $domainStatus = "No MX Records"; $statusDetails = "No MX records found for this domain." }
            }
        }
    } catch {
        Write-Host "    MX Records: N/A (Error retrieving MX: $($_.Exception.Message))"
        if ($domainStatus -eq "OK") {
            $domainStatus = "MX Lookup Error"
            $statusDetails = "An error occurred while looking up MX records: $($_.Exception.Message)."
        } else {
            $statusDetails += "`n    MX Records: N/A (Error: $($_.Exception.Message))."
        }
    }

    # --- Construct object for CSV export ---
    $csvObject = [PSCustomObject]@{
        DomainName          = $domain
        OverallStatus       = $domainStatus
        StatusDetails       = $statusDetails.Replace("`n", " ") # Remove newlines for CSV
        PublicNameservers   = $nsString
        ResolvedA_IPs       = ($resolvedA_Ips -join ', ') # A records resolved from public DNS
        ExpectedPleskIPs    = ($serverIps -join ', ') # Your Plesk server's IPs
        MX_Records          = ($mxIpAddressesReport -join '; ') # Formatted MX records with resolved IPs
    }
    $resultsForCsv += $csvObject

    # --- Report to console (current behavior) ---
    if ($domainStatus -ne "OK") {
        Write-Warning "    '$domain' is problematic: $domainStatus - $statusDetails"
    } else {
        Write-Host "    '$domain' resolves correctly to server IP and MX records are pointing correctly."
    }
}

# --- Export to CSV ---
Write-Host "`n--- Summary ---"
$outputCsvPath = "C:\Temp\PleskDomainDNS_Report_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"

if ($resultsForCsv.Count -gt 0) {
    $resultsForCsv | Export-Csv -Path $outputCsvPath -NoTypeInformation -Encoding UTF8
    Write-Host "DNS resolution report saved to $outputCsvPath"
} else {
    Write-Host "No domains processed or no data to export."
}

# The previous problematicDomains list (text file) is no longer generated,
# as all information is now in the CSV.
# If you still want the text file for *only* problematic domains, you can add:
# $resultsForCsv | Where-Object {$_.OverallStatus -ne "OK"} | Select-Object DomainName, OverallStatus, StatusDetails | Out-File "C:\Temp\ProblematicDomains_Windows.txt"

The Power of Knowing: Your Migration Blueprint

The best part? After running through all your domains, the script neatly compiles everything into a detailed CSV report. This isn’t just a bunch of techy jargon; it’s your go-to blueprint for a smooth migration. You’ll see:

  • Domain Name: Which domain are we talking about?
  • Overall Status: A quick glance to see if it’s “OK” or if there’s a “problem.”
  • Status Details: A human-friendly explanation of any issues found.
  • Public Nameservers: Where the domain’s primary DNS is managed.
  • Resolved A-IPs: Where the website currently points.
  • Expected Plesk IPs: What your current server’s IPs are (for comparison).
  • MX Records: Where the email for that domain is currently being sent.

This report lets you identify potential issues before they become actual problems, saving you time, stress, and potential downtime. A little preparation goes a long way, and with this script, you’ll be well on your way to a hassle-free Plesk migration!

No votes yet.
Please wait...

You may also like...

Leave a Reply

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

Moving Day for Your Website: How to Make Your Plesk Migration Smooth as Silk!

time to read: 6 min
0