207 lines
7.5 KiB
PowerShell
207 lines
7.5 KiB
PowerShell
# Migrate domains from *.huypham.vn to solutions.com.vn (Phase 6+ rebrand).
|
|
# Run on VPS with admin privilege. Idempotent - retry if fail mid-way.
|
|
#
|
|
# Workflow (3 IIS sites):
|
|
# 1. Add binding for new domain (port 80)
|
|
# 2. Run win-acme for Let's Encrypt cert via HTTP-01 challenge
|
|
# 3. Verify /health/ready + /health/live via new domain
|
|
# 4. Remove old binding (optional, via -RemoveOld)
|
|
#
|
|
# Usage:
|
|
# .\migrate-domains.ps1 # keep old binding, add new
|
|
# .\migrate-domains.ps1 -RemoveOld # remove old after verify
|
|
# .\migrate-domains.ps1 -SkipCert # skip win-acme step
|
|
#
|
|
# ASCII-only per gotcha #30 (PS 5.1 parser fail on UTF-8 diacritics).
|
|
|
|
[CmdletBinding()]
|
|
param(
|
|
[string]$NewApi = "api.solutions.com.vn",
|
|
[string]$NewAdmin = "admin.solutions.com.vn",
|
|
[string]$NewUser = "eoffice.solutions.com.vn",
|
|
[string]$OldApi = "api.huypham.vn",
|
|
[string]$OldAdmin = "admin.huypham.vn",
|
|
[string]$OldUser = "user.huypham.vn",
|
|
[string]$AdminEmail = "admin@huypham.vn",
|
|
[switch]$RemoveOld,
|
|
[switch]$SkipCert
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
Import-Module WebAdministration
|
|
|
|
function Write-Step($msg) { Write-Host "==> $msg" -ForegroundColor Cyan }
|
|
function Write-OK($msg) { Write-Host " [OK] $msg" -ForegroundColor Green }
|
|
function Write-Warn2($msg){ Write-Host " [!!] $msg" -ForegroundColor Yellow }
|
|
|
|
# ===================== 1. Pre-flight checks =====================
|
|
Write-Step "Pre-flight checks"
|
|
|
|
$vpsIp = "103.124.94.38"
|
|
try {
|
|
$vpsIp = (Invoke-WebRequest -Uri "https://api.ipify.org" -UseBasicParsing -TimeoutSec 5).Content.Trim()
|
|
} catch {
|
|
Write-Warn2 "Cannot detect public IP via ipify - fallback to hardcoded $vpsIp"
|
|
}
|
|
Write-Host " VPS public IP: $vpsIp"
|
|
|
|
foreach ($d in @($NewApi, $NewAdmin, $NewUser)) {
|
|
try {
|
|
$resolved = (Resolve-DnsName -Name $d -Type A -ErrorAction Stop | Select-Object -First 1).IPAddress
|
|
if ($resolved -eq $vpsIp) {
|
|
Write-OK "DNS $d = $resolved"
|
|
} else {
|
|
Write-Warn2 "DNS $d = $resolved (not matching VPS IP $vpsIp) - Let's Encrypt may fail"
|
|
}
|
|
} catch {
|
|
Write-Warn2 "DNS resolve FAIL for $d"
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
$siteMap = @(
|
|
@{ Site = "SolutionErp-Api"; Old = $OldApi; New = $NewApi },
|
|
@{ Site = "SolutionErp-Admin"; Old = $OldAdmin; New = $NewAdmin },
|
|
@{ Site = "SolutionErp-User"; Old = $OldUser; New = $NewUser }
|
|
)
|
|
foreach ($s in $siteMap) {
|
|
if (-not (Test-Path "IIS:\Sites\$($s.Site)")) {
|
|
Write-Error "Site '$($s.Site)' not found. Run setup-iis-sites.ps1 first."
|
|
exit 1
|
|
}
|
|
Write-OK "IIS site $($s.Site) ready"
|
|
}
|
|
|
|
# ===================== 2. Add new bindings (keep old) =====================
|
|
Write-Step "Add HTTP binding for 3 new domains"
|
|
|
|
foreach ($s in $siteMap) {
|
|
$site = $s.Site
|
|
$new = $s.New
|
|
|
|
$existingHttp = Get-WebBinding -Name $site -Protocol http -HostHeader $new -ErrorAction SilentlyContinue
|
|
if (-not $existingHttp) {
|
|
New-WebBinding -Name $site -IPAddress "*" -Port 80 -HostHeader $new -Protocol http | Out-Null
|
|
Write-OK "Add HTTP binding: $site *:80:$new"
|
|
} else {
|
|
Write-Host " HTTP binding exists: $site *:80:$new"
|
|
}
|
|
}
|
|
|
|
# ===================== 3. Request cert via win-acme =====================
|
|
if (-not $SkipCert) {
|
|
Write-Step "Request Let's Encrypt cert for 3 new domains"
|
|
|
|
$WacsExe = "C:\Program Files\win-acme\wacs.exe"
|
|
if (-not (Test-Path $WacsExe)) {
|
|
Write-Error "win-acme not installed. Run setup-ssl.ps1 first, or use -SkipCert."
|
|
exit 1
|
|
}
|
|
|
|
foreach ($s in $siteMap) {
|
|
Write-Host ""
|
|
Write-Host "==> Cert for $($s.New)" -ForegroundColor Cyan
|
|
|
|
$siteId = (Get-Website $s.Site).Id
|
|
$wacsArgs = @(
|
|
"--target", "manual",
|
|
"--host", $s.New,
|
|
"--store", "certificatestore",
|
|
"--installation", "iis",
|
|
"--installationsiteid", $siteId,
|
|
"--accepttos",
|
|
"--emailaddress", $AdminEmail
|
|
)
|
|
& $WacsExe @wacsArgs
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Warn2 "Cert $($s.New) FAIL exit $LASTEXITCODE - check:"
|
|
Write-Warn2 " 1. Port 80 internet -> VPS open?"
|
|
Write-Warn2 " 2. DNS $($s.New) -> $vpsIp?"
|
|
Write-Warn2 " 3. HTTP binding created?"
|
|
} else {
|
|
Write-OK "Cert $($s.New) installed (HTTPS binding + http->https)"
|
|
}
|
|
}
|
|
} else {
|
|
Write-Host "==> SkipCert flag - bypass win-acme" -ForegroundColor Yellow
|
|
}
|
|
|
|
# ===================== 4. Verify new endpoints =====================
|
|
Write-Step "Verify new endpoints"
|
|
|
|
Start-Sleep -Seconds 3
|
|
|
|
function Test-Endpoint($url) {
|
|
try {
|
|
$r = Invoke-WebRequest -Uri $url -UseBasicParsing -TimeoutSec 10 -SkipCertificateCheck
|
|
if ($r.StatusCode -eq 200) { Write-OK "$url = 200 OK"; return $true }
|
|
Write-Warn2 "$url = $($r.StatusCode) (expected 200)"; return $false
|
|
} catch {
|
|
Write-Warn2 "$url FAIL"
|
|
return $false
|
|
}
|
|
}
|
|
|
|
$apiLive = Test-Endpoint "https://$NewApi/health/live"
|
|
$apiReady = Test-Endpoint "https://$NewApi/health/ready"
|
|
$adminOk = Test-Endpoint "https://$NewAdmin"
|
|
$userOk = Test-Endpoint "https://$NewUser"
|
|
|
|
$allOk = $apiLive -and $apiReady -and $adminOk -and $userOk
|
|
|
|
# ===================== 5. Remove old bindings (optional) =====================
|
|
if ($RemoveOld) {
|
|
if (-not $allOk) {
|
|
Write-Warn2 "New endpoints not verified OK - skip removing old (keep fallback)"
|
|
} else {
|
|
Write-Step "Remove old bindings (huypham.vn)"
|
|
foreach ($s in $siteMap) {
|
|
$site = $s.Site
|
|
$old = $s.Old
|
|
|
|
$httpOld = Get-WebBinding -Name $site -Protocol http -HostHeader $old -ErrorAction SilentlyContinue
|
|
if ($httpOld) {
|
|
Remove-WebBinding -Name $site -Protocol http -HostHeader $old -Port 80
|
|
Write-OK "Removed HTTP *:80:$old from $site"
|
|
}
|
|
|
|
$httpsOld = Get-WebBinding -Name $site -Protocol https -HostHeader $old -ErrorAction SilentlyContinue
|
|
if ($httpsOld) {
|
|
Remove-WebBinding -Name $site -Protocol https -HostHeader $old -Port 443
|
|
Write-OK "Removed HTTPS *:443:$old from $site"
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
Write-Host ""
|
|
Write-Host "[INFO] Old bindings (*.huypham.vn) still active. Run with -RemoveOld after verify." -ForegroundColor Yellow
|
|
}
|
|
|
|
# ===================== 6. Summary =====================
|
|
Write-Host ""
|
|
Write-Host ("=" * 70) -ForegroundColor Cyan
|
|
Write-Host " MIGRATION DONE" -ForegroundColor Cyan
|
|
Write-Host ("=" * 70) -ForegroundColor Cyan
|
|
Write-Host ""
|
|
Write-Host "3 new domains:"
|
|
$statusApi = if ($apiLive) { "[OK]" } else { "[FAIL]" }
|
|
$statusReady = if ($apiReady) { "[OK]" } else { "[FAIL]" }
|
|
$statusAdmin = if ($adminOk) { "[OK]" } else { "[FAIL]" }
|
|
$statusUser = if ($userOk) { "[OK]" } else { "[FAIL]" }
|
|
Write-Host " - https://$NewApi/health/live $statusApi"
|
|
Write-Host " - https://$NewApi/health/ready $statusReady"
|
|
Write-Host " - https://$NewAdmin $statusAdmin"
|
|
Write-Host " - https://$NewUser $statusUser"
|
|
Write-Host ""
|
|
Write-Host "Next:"
|
|
Write-Host " 1. Trigger CI/CD redeploy (push empty commit)"
|
|
Write-Host " - BE rebuild with new CORS"
|
|
Write-Host " - FE rebuild with VITE_API_BASE_URL=https://$NewApi"
|
|
Write-Host " 2. Test login via browser"
|
|
Write-Host " 3. After 1-2 days verify stable: .\migrate-domains.ps1 -RemoveOld -SkipCert"
|
|
Write-Host ""
|
|
if (-not $allOk) {
|
|
Write-Warn2 "Some endpoints failed - check Event Log and IIS log before proceed."
|
|
exit 1
|
|
}
|