[CLAUDE] Scripts: fix migrate-domains.ps1 ASCII-only (gotcha #30 PS 5.1)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m46s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m46s
This commit is contained in:
@ -1,24 +1,18 @@
|
||||
# Migrate domains from *.huypham.vn to solutions.com.vn (Phase 6+ rebrand).
|
||||
# Run trên VPS với admin privilege. Idempotent — chạy lại nếu fail giữa chừng.
|
||||
# Run on VPS with admin privilege. Idempotent - retry if fail mid-way.
|
||||
#
|
||||
# Workflow (3 IIS sites):
|
||||
# 1. Add binding mới cho domain mới (*:80 + *:443 với cert self-signed tạm)
|
||||
# 2. Run win-acme xin cert Let's Encrypt qua HTTP-01 challenge (port 80)
|
||||
# 3. Verify /health/ready + /health/live qua domain mới
|
||||
# 4. Remove binding cũ (*:huypham.vn + *:user.huypham.vn) — OPTIONAL via -RemoveOld
|
||||
# 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 # giữ binding cũ, thêm mới
|
||||
# .\migrate-domains.ps1 -RemoveOld # xóa binding cũ sau verify
|
||||
# .\migrate-domains.ps1 -SkipCert # bỏ bước win-acme (nếu anh đã có cert)
|
||||
# .\migrate-domains.ps1 # keep old binding, add new
|
||||
# .\migrate-domains.ps1 -RemoveOld # remove old after verify
|
||||
# .\migrate-domains.ps1 -SkipCert # skip win-acme step
|
||||
#
|
||||
# Prereq:
|
||||
# - DNS 3 domain mới đã trỏ 103.124.94.38 (user confirm)
|
||||
# - Port 80+443 firewall open cho internet
|
||||
# - win-acme đã cài (script setup-ssl.ps1 cài sẵn)
|
||||
# - IIS binding cũ còn active (huypham.vn)
|
||||
#
|
||||
# Rollback: nếu fail, binding cũ vẫn còn → site vẫn phục vụ qua *.huypham.vn
|
||||
# ASCII-only per gotcha #30 (PS 5.1 parser fail on UTF-8 diacritics).
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
@ -43,26 +37,28 @@ function Write-Warn2($msg){ Write-Host " [!!] $msg" -ForegroundColor Yellow }
|
||||
# ===================== 1. Pre-flight checks =====================
|
||||
Write-Step "Pre-flight checks"
|
||||
|
||||
# 1a. Verify DNS của 3 domain mới trỏ đúng IP VPS
|
||||
$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"
|
||||
Write-OK "DNS $d = $resolved"
|
||||
} else {
|
||||
Write-Warn2 "DNS $d -> $resolved (KHONG KHOP VPS IP $vpsIp) — Let's Encrypt se fail!"
|
||||
Read-Host "Continue anyway? (Ctrl+C abort, Enter continue)"
|
||||
Write-Warn2 "DNS $d = $resolved (not matching VPS IP $vpsIp) - Let's Encrypt may fail"
|
||||
}
|
||||
} catch {
|
||||
Write-Warn2 "DNS resolve FAIL cho $d — $_"
|
||||
Write-Warn2 "DNS resolve FAIL for $d"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# 1b. Verify 3 IIS site tồn tại
|
||||
$siteMap = @(
|
||||
@{ Site = "SolutionErp-Api"; Old = $OldApi; New = $NewApi },
|
||||
@{ Site = "SolutionErp-Admin"; Old = $OldAdmin; New = $NewAdmin },
|
||||
@ -70,20 +66,19 @@ $siteMap = @(
|
||||
)
|
||||
foreach ($s in $siteMap) {
|
||||
if (-not (Test-Path "IIS:\Sites\$($s.Site)")) {
|
||||
Write-Error "Site '$($s.Site)' khong ton tai. Chay setup-iis-sites.ps1 truoc."
|
||||
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 binding moi cho 3 site (giu binding cu)"
|
||||
Write-Step "Add HTTP binding for 3 new domains"
|
||||
|
||||
foreach ($s in $siteMap) {
|
||||
$site = $s.Site
|
||||
$new = $s.New
|
||||
|
||||
# Check binding HTTP (port 80) cho domain moi
|
||||
$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
|
||||
@ -93,19 +88,19 @@ foreach ($s in $siteMap) {
|
||||
}
|
||||
}
|
||||
|
||||
# ===================== 3. Request cert Let's Encrypt (win-acme) =====================
|
||||
# ===================== 3. Request cert via win-acme =====================
|
||||
if (-not $SkipCert) {
|
||||
Write-Step "Request cert Let's Encrypt cho 3 domain moi"
|
||||
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 chua cai. Run setup-ssl.ps1 truoc, hoac dùng -SkipCert."
|
||||
Write-Error "win-acme not installed. Run setup-ssl.ps1 first, or use -SkipCert."
|
||||
exit 1
|
||||
}
|
||||
|
||||
foreach ($s in $siteMap) {
|
||||
Write-Host ""
|
||||
Write-Host "==> Issue cert $($s.New)" -ForegroundColor Cyan
|
||||
Write-Host "==> Cert for $($s.New)" -ForegroundColor Cyan
|
||||
|
||||
$siteId = (Get-Website $s.Site).Id
|
||||
$wacsArgs = @(
|
||||
@ -119,59 +114,57 @@ if (-not $SkipCert) {
|
||||
)
|
||||
& $WacsExe @wacsArgs
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Warn2 "Cert $($s.New) FAIL exit $LASTEXITCODE — skip, check:"
|
||||
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 $($s.Site) *:80:$($s.New) created?"
|
||||
Write-Warn2 " 3. HTTP binding created?"
|
||||
} else {
|
||||
Write-OK "Cert $($s.New) installed (auto HTTPS binding + http->https redirect)"
|
||||
Write-OK "Cert $($s.New) installed (HTTPS binding + http->https)"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Write-Host "==> SkipCert flag — bo qua xin cert Let's Encrypt" -ForegroundColor Yellow
|
||||
Write-Host "==> SkipCert flag - bypass win-acme" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
# ===================== 4. Verify endpoint moi =====================
|
||||
Write-Step "Verify endpoint moi"
|
||||
# ===================== 4. Verify new endpoints =====================
|
||||
Write-Step "Verify new endpoints"
|
||||
|
||||
Start-Sleep -Seconds 3 # cho IIS reload binding
|
||||
Start-Sleep -Seconds 3
|
||||
|
||||
function Test-Endpoint($url, $expectOk) {
|
||||
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
|
||||
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: $_"
|
||||
Write-Warn2 "$url FAIL"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
$apiLive = Test-Endpoint "https://$NewApi/health/live" $true
|
||||
$apiReady = Test-Endpoint "https://$NewApi/health/ready" $true
|
||||
$adminOk = Test-Endpoint "https://$NewAdmin" $true
|
||||
$userOk = Test-Endpoint "https://$NewUser" $true
|
||||
$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 "Endpoint moi CHUA verify OK — skip remove old binding (giu fallback)"
|
||||
Write-Warn2 "New endpoints not verified OK - skip removing old (keep fallback)"
|
||||
} else {
|
||||
Write-Step "Remove binding cu (huypham.vn)"
|
||||
Write-Step "Remove old bindings (huypham.vn)"
|
||||
foreach ($s in $siteMap) {
|
||||
$site = $s.Site
|
||||
$old = $s.Old
|
||||
|
||||
# Remove HTTP
|
||||
$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"
|
||||
}
|
||||
|
||||
# Remove HTTPS
|
||||
$httpsOld = Get-WebBinding -Name $site -Protocol https -HostHeader $old -ErrorAction SilentlyContinue
|
||||
if ($httpsOld) {
|
||||
Remove-WebBinding -Name $site -Protocol https -HostHeader $old -Port 443
|
||||
@ -181,30 +174,33 @@ if ($RemoveOld) {
|
||||
}
|
||||
} else {
|
||||
Write-Host ""
|
||||
Write-Host "[INFO] Binding cu (*.huypham.vn) van giu active. Run -RemoveOld sau khi verify xong." -ForegroundColor Yellow
|
||||
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 ("=" * 70) -ForegroundColor Cyan
|
||||
Write-Host " MIGRATION DONE" -ForegroundColor Cyan
|
||||
Write-Host "=" * 70 -ForegroundColor Cyan
|
||||
Write-Host ("=" * 70) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "3 domain moi:"
|
||||
Write-Host " - https://$NewApi/health/live $(if ($apiLive) {'[OK]'} else {'[FAIL]'})"
|
||||
Write-Host " - https://$NewApi/health/ready $(if ($apiReady) {'[OK]'} else {'[FAIL]'})"
|
||||
Write-Host " - https://$NewAdmin $(if ($adminOk) {'[OK]'} else {'[FAIL]'})"
|
||||
Write-Host " - https://$NewUser $(if ($userOk) {'[OK]'} else {'[FAIL]'})"
|
||||
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 step:"
|
||||
Write-Host " 1. Trigger CI/CD redeploy (push empty commit hoac manual workflow dispatch)"
|
||||
Write-Host " → BE rebuild voi CORS moi (allow https://admin.solutions.com.vn + eoffice.solutions.com.vn)"
|
||||
Write-Host " → FE rebuild voi VITE_API_BASE_URL=https://api.solutions.com.vn"
|
||||
Write-Host " 2. Test login qua browser: https://$NewAdmin + https://$NewUser"
|
||||
Write-Host " 3. Sau 1-2 ngay verify stable → chay lai script voi -RemoveOld"
|
||||
Write-Host " .\migrate-domains.ps1 -RemoveOld -SkipCert"
|
||||
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 "CO ENDPOINT FAIL — kiem tra Event Log + IIS log truoc khi proceed."
|
||||
Write-Warn2 "Some endpoints failed - check Event Log and IIS log before proceed."
|
||||
exit 1
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user