# Install HTTPS cert Let's Encrypt for 3 SOLUTION_ERP domains via win-acme (WACS). # Run on VPS Windows Server with admin privilege. # Idempotent: re-run skips cert still valid. # # Usage: # .\setup-ssl.ps1 # # Prereq: # - IIS sites created (run setup-iis-sites.ps1 first) # - Port 80 from Internet -> VPS open (Let's Encrypt HTTP-01 challenge) # - 3 domains api/admin/eoffice.solutions.com.vn pointing DNS to VPS IP # # Output: # - 3 cert in Windows Cert Store (LocalMachine\My) # - HTTPS binding port 443 for 3 sites # - Scheduled task auto-renew (90 day cycle Let's Encrypt, win-acme auto renew when 30 days left) $ErrorActionPreference = 'Stop' $WacsDir = "C:\Program Files\win-acme" $WacsExe = Join-Path $WacsDir "wacs.exe" # ===================== 1. Download + install win-acme ===================== if (-not (Test-Path $WacsExe)) { Write-Host "==> Download win-acme (WACS)..." -ForegroundColor Cyan $url = "https://github.com/win-acme/win-acme/releases/download/v2.2.9.1701/win-acme.v2.2.9.1701.x64.trimmed.zip" $zip = "$env:TEMP\wacs.zip" Invoke-WebRequest -Uri $url -OutFile $zip -UseBasicParsing if (-not (Test-Path $WacsDir)) { New-Item -ItemType Directory -Force -Path $WacsDir | Out-Null } Expand-Archive -Path $zip -DestinationPath $WacsDir -Force Remove-Item $zip Write-Host " Installed to $WacsDir" } else { Write-Host "==> win-acme already installed at $WacsDir" } # ===================== 2. Check IIS sites exist ===================== Import-Module WebAdministration $domains = @( @{ Site = "SolutionErp-Api"; HostName = "api.solutions.com.vn" }, @{ Site = "SolutionErp-Admin"; HostName = "admin.solutions.com.vn" }, @{ Site = "SolutionErp-User"; HostName = "eoffice.solutions.com.vn" } ) foreach ($d in $domains) { if (-not (Test-Path "IIS:\Sites\$($d.Site)")) { Write-Error "Site '$($d.Site)' does not exist. Run setup-iis-sites.ps1 first." exit 1 } } Write-Host " 3 IIS sites ready" # ===================== 3. Run win-acme per domain ===================== foreach ($d in $domains) { Write-Host "" Write-Host "==> Issue cert for $($d.HostName)" -ForegroundColor Cyan # win-acme CLI non-interactive: # --target manual + --host # --siteid -> IIS site to install on # --installation iis -> auto bind HTTPS 443 + http->https redirect # --accepttos -> accept Let's Encrypt terms # --emailaddress -> contact email for expiry alerts $siteId = (Get-Website $d.Site).Id $wacsArgs = @( "--target", "manual", "--host", $d.HostName, "--store", "certificatestore", "--installation", "iis", "--installationsiteid", $siteId, "--accepttos", "--emailaddress", "admin@huypham.vn" ) & $WacsExe @wacsArgs if ($LASTEXITCODE -ne 0) { Write-Warning "Issue cert for $($d.HostName) FAIL exit $LASTEXITCODE - check:" Write-Warning " 1. Port 80 Internet -> VPS open (Let's Encrypt reach via HTTP-01)?" Write-Warning " 2. DNS $($d.HostName) -> $((Resolve-DnsName $d.HostName -Type A -ErrorAction SilentlyContinue).IPAddress)?" Write-Warning " 3. IIS site $($d.Site) binding port 80 with host header $($d.HostName)?" } else { Write-Host " [OK] Cert installed" } } # ===================== 4. HTTP -> HTTPS redirect ===================== Write-Host "" Write-Host "==> HTTP -> HTTPS redirect (win-acme auto-adds via --installation iis)" -ForegroundColor Cyan Write-Host " (skip manual rule - win-acme handled it)" # ===================== 5. Verify scheduled task ===================== Write-Host "" Write-Host "==> Verify scheduled task auto-renew" $task = Get-ScheduledTask -TaskName "win-acme renew (acme-v02.api.letsencrypt.org)" -ErrorAction SilentlyContinue if ($task) { Write-Host " [OK] Task '$($task.TaskName)' exists - auto renew 9h daily" } else { Write-Warning " Task not created - run manually: $WacsExe --renew --baseuri https://acme-v02.api.letsencrypt.org/" } Write-Host "" Write-Host "[OK] SSL setup DONE" -ForegroundColor Green Write-Host " Test: openssl s_client -connect api.solutions.com.vn:443 < /dev/null | openssl x509 -noout -subject -dates" Write-Host " Or browser: https://api.solutions.com.vn/health/live"