#requires -Version 5.1 #requires -RunAsAdministrator <# .SYNOPSIS Source-side transfer: backup IIS state + robocopy snolla site to RUVDS. .DESCRIPTION Run on source (DESKTOP-NSEF0UK / windows-recovery-host) in PowerShell as Administrator. Steps: 1. Probe RUVDS SMB 445 reachability (fail fast). 2. Pull RUVDS Administrator password from pass-store. 3. Backup IIS state to C:\Users\vitya\iis-backup-pre-ruvds\: - applicationHost.config - appcmd dump of all sites (for reference) - appcmd dump of snolla site config + apppool (for recreate on RUVDS) 4. net-use mount \\80.64.31.36\sites with creds. 5. Copy IIS backup files into the share (so RUVDS can grab them for recreate). 6. robocopy C:\sites\snolla -> \\80.64.31.36\sites\snolla (/Z resume-on-disconnect, /MT:8 parallel, /R:2 /W:5 retries, /XJ skip junctions) 7. Verify count + size match. 8. Disconnect SMB. Transcript: C:\Users\vitya\iis-backup-pre-ruvds\transfer-log.txt #> [CmdletBinding()] param( [string]$RuvdsIp = '80.64.31.36', [string]$ShareName = 'sites', [string]$SiteName = 'snolla', [string]$SourceSite = 'C:\sites\snolla', [string]$BackupDir = 'C:\Users\vitya\iis-backup-pre-ruvds' ) $ErrorActionPreference = 'Stop' $ProgressPreference = 'SilentlyContinue' if (-not (Test-Path $BackupDir)) { New-Item -ItemType Directory -Path $BackupDir -Force | Out-Null } Start-Transcript -Path (Join-Path $BackupDir 'transfer-log.txt') -Append -Force | Out-Null function Step($name) { Write-Host "`n=== $name ===" -ForegroundColor Cyan } function Ok($msg) { Write-Host " [OK] $msg" -ForegroundColor Green } function Skip($msg) { Write-Host " [SKIP] $msg" -ForegroundColor Yellow } function Fail($msg) { Write-Host " [FAIL] $msg" -ForegroundColor Red } $shareUnc = "\\$RuvdsIp\$ShareName" $mountedShare = $false try { Step '1. Probe RUVDS SMB 445' $smb = Test-NetConnection -ComputerName $RuvdsIp -Port 445 -InformationLevel Quiet -WarningAction SilentlyContinue if (-not $smb) { throw "RUVDS:445 not reachable. Re-run 01-ruvds-bootstrap.ps1 on RUVDS." } Ok "RUVDS:445 reachable" Step '2. Pull RUVDS password from pass-store' $bash = 'C:\Program Files\Git\bin\bash.exe' if (-not (Test-Path $bash)) { throw "bash.exe not found at $bash; pass-store unreachable" } $passOut = & $bash -lc 'pass show ruvds-iis/full-env' $passLine = $passOut | Where-Object { $_ -match '^RUVDS_IIS_PASS=' } | Select-Object -First 1 if (-not $passLine) { throw "RUVDS_IIS_PASS not found in pass show ruvds-iis/full-env" } $ruvdsPass = $passLine -replace '^RUVDS_IIS_PASS=','' Ok "credentials loaded from pass-store" Step '3. Backup source IIS state' $appcmd = "$env:SystemRoot\system32\inetsrv\appcmd.exe" if (-not (Test-Path $appcmd)) { throw "appcmd.exe not found -- IIS Management Tools not installed on source?" } Copy-Item 'C:\Windows\System32\inetsrv\config\applicationHost.config' (Join-Path $BackupDir 'applicationHost.config') -Force Ok "applicationHost.config backed up" & $appcmd list site /config:* /xml | Out-File -FilePath (Join-Path $BackupDir 'all-sites.xml') -Encoding UTF8 Ok "appcmd list site (all) dumped" & $appcmd list site /name:$SiteName /config:* /xml | Out-File -FilePath (Join-Path $BackupDir 'snolla-site.xml') -Encoding UTF8 Ok "snolla-site.xml dumped" & $appcmd list apppool /xml | Out-File -FilePath (Join-Path $BackupDir 'all-apppools.xml') -Encoding UTF8 Ok "all-apppools.xml dumped" Step '4. Mount RUVDS share' cmd.exe /c "net use $shareUnc /user:Administrator $ruvdsPass" 2>&1 | Out-Null if ($LASTEXITCODE -ne 0) { cmd.exe /c "net use $shareUnc /delete /y" 2>&1 | Out-Null cmd.exe /c "net use $shareUnc /user:Administrator $ruvdsPass" 2>&1 | Tee-Object -Variable mountOut | Out-Null if ($LASTEXITCODE -ne 0) { throw "net use failed: $mountOut" } } $mountedShare = $true Ok "mounted $shareUnc" Step '5. Copy IIS backup files to share (for RUVDS recreate-script access)' $remoteBackupDir = Join-Path $shareUnc '_iis-backup' if (-not (Test-Path $remoteBackupDir)) { New-Item -ItemType Directory -Path $remoteBackupDir -Force | Out-Null } Copy-Item (Join-Path $BackupDir '*.xml') $remoteBackupDir -Force Copy-Item (Join-Path $BackupDir 'applicationHost.config') $remoteBackupDir -Force Ok "IIS backup files copied to $remoteBackupDir" Step '6. robocopy snolla site' $srcDir = $SourceSite $dstDir = Join-Path $shareUnc $SiteName if (-not (Test-Path $srcDir)) { throw "source $srcDir not found" } $srcSizeGB = [math]::Round(((Get-ChildItem $srcDir -Recurse -File -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum / 1GB), 2) Write-Host " Source size: $srcSizeGB GB" Write-Host " Destination: $dstDir" Write-Host " This may take 30-90 min on home uplink. Progress goes to log." Write-Host "" $robocopyLog = Join-Path $BackupDir 'robocopy-snolla.log' $robocopyArgs = @( $srcDir, $dstDir, '*.*', '/S', '/E', '/DCOPY:DA', '/COPY:DAT', '/Z', '/MT:8', '/R:2', '/W:5', '/XJ', '/TEE', "/LOG+:$robocopyLog", '/NP', '/NDL' ) $rcStart = Get-Date & robocopy.exe @robocopyArgs $rcExit = $LASTEXITCODE $rcDuration = (Get-Date) - $rcStart # robocopy exit codes: 0-7 = success/partial, 8+ = error if ($rcExit -ge 8) { throw "robocopy failed with exit $rcExit. See $robocopyLog tail." } Ok ("robocopy done in {0:hh\:mm\:ss} (exit={1})" -f $rcDuration, $rcExit) Step '7. Verify count + size' $srcCount = (Get-ChildItem $srcDir -Recurse -File -ErrorAction SilentlyContinue | Measure-Object).Count $dstCount = (Get-ChildItem $dstDir -Recurse -File -ErrorAction SilentlyContinue | Measure-Object).Count $srcBytes = (Get-ChildItem $srcDir -Recurse -File -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum $dstBytes = (Get-ChildItem $dstDir -Recurse -File -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum Write-Host " Source: $srcCount files, $([math]::Round($srcBytes / 1MB, 0)) MB" Write-Host " Dest: $dstCount files, $([math]::Round($dstBytes / 1MB, 0)) MB" if ($srcCount -ne $dstCount) { Fail "FILE COUNT MISMATCH ($srcCount vs $dstCount)"; throw "count mismatch" } if ($srcBytes -ne $dstBytes) { Fail "SIZE MISMATCH ($srcBytes vs $dstBytes bytes)"; throw "size mismatch" } Ok "count + size match" Step '8. Summary' Write-Host "" Write-Host " Source site: $srcDir" Write-Host " RUVDS dest: $dstDir" Write-Host " Files transferred: $dstCount" Write-Host " Bytes transferred: $([math]::Round($dstBytes / 1GB, 2)) GB" Write-Host " Duration: $('{0:hh\:mm\:ss}' -f $rcDuration)" Write-Host " IIS backup local: $BackupDir" Write-Host " IIS backup remote: $remoteBackupDir (on RUVDS as C:\sites\_iis-backup\)" Write-Host "" Write-Host " Transcript: $(Join-Path $BackupDir 'transfer-log.txt')" -ForegroundColor DarkGray Write-Host " Robocopy log: $robocopyLog" -ForegroundColor DarkGray Write-Host "" Write-Host " Next: run 03-ruvds-recreate-sites.ps1 on RUVDS." -ForegroundColor Cyan } catch { Fail $_.Exception.Message Write-Host $_.ScriptStackTrace -ForegroundColor DarkRed exit 1 } finally { if ($mountedShare) { cmd.exe /c "net use $shareUnc /delete /y" 2>&1 | Out-Null Write-Host "`n [cleanup] SMB share dismounted" -ForegroundColor DarkGray } Stop-Transcript | Out-Null }