Skip to main content

How to Perform a Blue-Green Deployment Switchover in Batch Script

Blue-green deployment is a release strategy that reduces downtime and risk by maintaining two identical production environments: Blue (the current live environment) and Green (the new version). Traffic is routed to one environment at a time. When a new release is ready, you deploy to the inactive environment, verify it, and then switch traffic instantly. If something goes wrong, you switch back to the previous environment in seconds.

In this guide, we will explore how to implement blue-green deployment switchover logic in Batch Script for IIS-based web applications and general file-based deployments.

Understanding Blue-Green Architecture

+------------+
Users -------> | Load |
| Balancer |
| / Symlink |
+-----+------+
|
+---------+--------+
| |
+-----v-----+ +-----v-----+
| BLUE | | GREEN |
| v1.0 | | v1.1 |
| (LIVE) | | (IDLE) |
+-----------+ +-----------+

The switchover changes which environment receives traffic:

  1. Deploy to the idle environment.
  2. Test the idle environment.
  3. Switch traffic to the newly deployed environment.
  4. The old live environment becomes the new idle.

The simplest approach uses a symbolic link that points to the active deployment directory:

@echo off
setlocal enabledelayedexpansion

set "live_link=C:\WebApps\Production"
set "blue_dir=C:\WebApps\Blue"
set "green_dir=C:\WebApps\Green"
set "state_file=C:\WebApps\.active_env"

:: Verify Admin (symlinks require elevation)
net session >nul 2>&1
if %errorlevel% neq 0 (
echo [ERROR] Administrator privileges required.
pause
exit /b 1
)

echo =============================================
echo BLUE-GREEN SWITCHOVER
echo =============================================
echo.

:: Determine which environment is active
set "active="
if exist "%state_file%" (
set /p "active=" < "%state_file%"
)

if /i "!active!"=="Blue" (
set "active=Blue"
set "inactive=Green"
set "switch_to=%green_dir%"
) else if /i "!active!"=="Green" (
set "active=Green"
set "inactive=Blue"
set "switch_to=%blue_dir%"
) else (
:: First run or unknown state: check symlink target
set "active=Unknown"
set "inactive=Blue"
set "switch_to=%blue_dir%"
if exist "%live_link%" (
dir "%live_link%" /al 2>nul | findstr /i "Blue" >nul
if !errorlevel!==0 (
set "active=Blue"
set "inactive=Green"
set "switch_to=%green_dir%"
) else (
dir "%live_link%" /al 2>nul | findstr /i "Green" >nul
if !errorlevel!==0 (
set "active=Green"
set "inactive=Blue"
set "switch_to=%blue_dir%"
)
)
)
)

echo Current LIVE: !active!
echo Will switch to: !inactive!
echo.

:: Verify the target directory exists
if not exist "!switch_to!" (
echo [ERROR] Target directory does not exist: !switch_to!
pause
exit /b 1
)

set /p "confirm=Perform switchover? (YES/NO): "
if /i not "!confirm!"=="YES" (
echo [CANCELLED]
pause
exit /b 0
)

echo.
echo Switching...

:: Remove current symlink
rmdir "%live_link%" 2>nul

:: Create new symlink pointing to the other environment
mklink /d "%live_link%" "!switch_to!"

if !errorlevel!==0 (
:: Record the new active environment
>"%state_file%" echo !inactive!
echo.
echo [SUCCESS] Traffic now routed to !inactive!.
echo %live_link% -^> !switch_to!
) else (
echo [ERROR] Switchover failed!
echo [EMERGENCY] Attempting to restore previous symlink...
if /i "!active!"=="Blue" (
mklink /d "%live_link%" "%blue_dir%"
) else if /i "!active!"=="Green" (
mklink /d "%live_link%" "%green_dir%"
)
)

pause

Method 2: IIS-Based Blue-Green Switchover

Switch between two IIS websites by stopping one and starting the other:

@echo off
setlocal enabledelayedexpansion

set "blue_site=WebApp-Blue"
set "green_site=WebApp-Green"
set "appcmd=%SystemRoot%\system32\inetsrv\appcmd.exe"

net session >nul 2>&1
if %errorlevel% neq 0 (
echo [ERROR] Admin required.
pause
exit /b 1
)

if not exist "%appcmd%" (
echo [ERROR] IIS appcmd.exe not found. Is IIS installed?
pause
exit /b 1
)

echo =============================================
echo IIS BLUE-GREEN SWITCHOVER
echo =============================================
echo.

:: Determine which site is running
set "blue_running="
set "green_running="
for /f "delims=" %%S in ('"%appcmd%" list site /state:Started /text:SITE.NAME 2^>nul') do (
if "%%S"=="%blue_site%" set "blue_running=1"
if "%%S"=="%green_site%" set "green_running=1"
)

if defined blue_running (
set "active=Blue"
set "stop_site=%blue_site%"
set "start_site=%green_site%"
set "new_active=Green"
) else if defined green_running (
set "active=Green"
set "stop_site=%green_site%"
set "start_site=%blue_site%"
set "new_active=Blue"
) else (
echo [ERROR] Neither Blue nor Green site is running.
pause
exit /b 1
)

echo Current LIVE: !active!
echo Switching to: !new_active!
echo.
set /p "confirm=Proceed? (YES/NO): "
if /i not "!confirm!"=="YES" exit /b 0

:: Perform the switchover: start new first, then stop old
echo.
echo [1/2] Starting !new_active! site...
"%appcmd%" start site /site.name:"%start_site%"

if !errorlevel! neq 0 (
echo [ERROR] Failed to start %start_site%. Aborting switchover.
echo !active! remains LIVE.
pause
exit /b 1
)

echo [2/2] Stopping !active! site...
"%appcmd%" stop site /site.name:"%stop_site%"

if !errorlevel! neq 0 (
echo [WARNING] Failed to stop %stop_site%. Both sites may be running.
echo Verify manually with: appcmd list site
)

echo.
echo [SUCCESS] !new_active! is now LIVE.

pause

Method 3: Full Deployment and Switchover Workflow

Deploy to the idle environment, verify, then switch:

@echo off
setlocal enabledelayedexpansion

:: Configuration
set "blue_dir=C:\WebApps\Blue"
set "green_dir=C:\WebApps\Green"
set "live_link=C:\WebApps\Production"
set "state_file=C:\WebApps\.active_env"
set "source=C:\Build\Publish\WebApp"
set "health_url=http://localhost:8081/health"

net session >nul 2>&1
if %errorlevel% neq 0 (
echo [ERROR] Admin required.
pause
exit /b 1
)

:: Verify source exists
if not exist "%source%" (
echo [ERROR] Build artifacts not found: %source%
pause
exit /b 1
)

echo =============================================
echo DEPLOY AND SWITCH
echo =============================================
echo.

:: Determine idle environment
set "active_name=Unknown"
set "target=%blue_dir%"
set "target_name=Blue"

if exist "%state_file%" (
set /p "active_env=" < "%state_file%"
)

if /i "!active_env!"=="Blue" (
set "active_name=Blue"
set "target=%green_dir%"
set "target_name=Green"
) else if /i "!active_env!"=="Green" (
set "active_name=Green"
set "target=%blue_dir%"
set "target_name=Blue"
) else (
:: Fall back to symlink inspection
dir "%live_link%" /al 2>nul | findstr /i "Blue" >nul
if !errorlevel!==0 (
set "active_name=Blue"
set "target=%green_dir%"
set "target_name=Green"
) else (
dir "%live_link%" /al 2>nul | findstr /i "Green" >nul
if !errorlevel!==0 (
set "active_name=Green"
set "target=%blue_dir%"
set "target_name=Blue"
)
)
)

echo Active: !active_name!
echo Deploy to: !target_name! (!target!^)
echo.

:: Step 1: Deploy to idle environment
echo [1/3] Deploying to !target_name!...
robocopy "%source%" "!target!" /mir /r:3 /w:5 /np /ndl /nfl >nul
set "robo_rc=!errorlevel!"
if !robo_rc! gtr 7 (
echo [ABORT] Deployment failed ^(Robocopy exit code: !robo_rc!^).
pause
exit /b 1
)
echo Deployed successfully.

:: Step 2: Health check
echo [2/3] Running health check...
set "health_ok=0"
powershell -noprofile -command ^
"try { ^
$r = Invoke-WebRequest -Uri '%health_url%' -UseBasicParsing -TimeoutSec 10; ^
if ($r.StatusCode -eq 200) { ^
Write-Host ' Health check passed.'; ^
exit 0 ^
} else { ^
Write-Host (' [FAIL] Status: ' + $r.StatusCode); ^
exit 1 ^
} ^
} catch { ^
Write-Host (' [FAIL] ' + $_.Exception.Message); ^
exit 1 ^
}"
if !errorlevel!==0 (
set "health_ok=1"
) else (
echo.
set /p "force=Health check failed. Switch anyway? (YES/NO): "
if /i not "!force!"=="YES" (
echo [ABORT] Switchover cancelled. !target_name! has the new code but is not live.
pause
exit /b 1
)
)

:: Step 3: Switchover
echo [3/3] Switching traffic...
rmdir "%live_link%" 2>nul
mklink /d "%live_link%" "!target!"

if !errorlevel!==0 (
>"%state_file%" echo !target_name!
echo.
echo [SUCCESS] !target_name! is now LIVE.
echo Previous: !active_name! (now idle^)
) else (
echo [ERROR] Switchover failed. Attempting to restore...
if /i "!active_name!"=="Blue" (
mklink /d "%live_link%" "%blue_dir%"
) else if /i "!active_name!"=="Green" (
mklink /d "%live_link%" "%green_dir%"
)
pause
exit /b 1
)

pause

Method 4: Quick Rollback

If the new deployment has issues, instantly switch back:

@echo off
setlocal enabledelayedexpansion

set "blue_dir=C:\WebApps\Blue"
set "green_dir=C:\WebApps\Green"
set "live_link=C:\WebApps\Production"
set "state_file=C:\WebApps\.active_env"

net session >nul 2>&1
if %errorlevel% neq 0 (
echo [ERROR] Admin required.
pause
exit /b 1
)

:: Determine current and previous
set "current=Unknown"
set "rollback_to=%blue_dir%"
set "rollback_name=Blue"

if exist "%state_file%" (
set /p "active_env=" < "%state_file%"
)

if /i "!active_env!"=="Blue" (
set "current=Blue"
set "rollback_to=%green_dir%"
set "rollback_name=Green"
) else if /i "!active_env!"=="Green" (
set "current=Green"
set "rollback_to=%blue_dir%"
set "rollback_name=Blue"
) else (
dir "%live_link%" /al 2>nul | findstr /i "Blue" >nul
if !errorlevel!==0 (
set "current=Blue"
set "rollback_to=%green_dir%"
set "rollback_name=Green"
) else (
dir "%live_link%" /al 2>nul | findstr /i "Green" >nul
if !errorlevel!==0 (
set "current=Green"
set "rollback_to=%blue_dir%"
set "rollback_name=Blue"
)
)
)

if "!current!"=="Unknown" (
echo [ERROR] Cannot determine current environment. Check symlink manually.
echo %live_link%
pause
exit /b 1
)

:: Verify rollback target exists
if not exist "!rollback_to!" (
echo [ERROR] Rollback target does not exist: !rollback_to!
pause
exit /b 1
)

echo =============================================
echo EMERGENCY ROLLBACK
echo =============================================
echo Current LIVE: !current!
echo Roll back to: !rollback_name!
echo.

set /p "confirm=ROLLBACK NOW? (YES/NO): "
if /i not "!confirm!"=="YES" exit /b 0

rmdir "%live_link%" 2>nul
mklink /d "%live_link%" "!rollback_to!"

if !errorlevel!==0 (
>"%state_file%" echo !rollback_name!
echo.
echo [ROLLBACK COMPLETE] !rollback_name! is now LIVE.
) else (
echo [ERROR] Rollback failed! Manual intervention required.
echo Recreate symlink: mklink /d "%live_link%" "!rollback_to!"
)

pause

Method 5: Status Dashboard

@echo off
setlocal enabledelayedexpansion

set "blue_dir=C:\WebApps\Blue"
set "green_dir=C:\WebApps\Green"
set "live_link=C:\WebApps\Production"
set "state_file=C:\WebApps\.active_env"

echo =============================================
echo BLUE-GREEN STATUS
echo =============================================
echo.

:: Check which is active
set "blue_status=IDLE"
set "green_status=IDLE"
set "link_ok=0"

if exist "%state_file%" (
set /p "active_env=" < "%state_file%"
)

if /i "!active_env!"=="Blue" (
set "blue_status=LIVE"
set "link_ok=1"
) else if /i "!active_env!"=="Green" (
set "green_status=LIVE"
set "link_ok=1"
)

if "!link_ok!"=="0" (
:: Fall back to symlink inspection
if exist "%live_link%" (
dir "%live_link%" /al 2>nul | findstr /i "Blue" >nul
if !errorlevel!==0 (
set "blue_status=LIVE"
) else (
dir "%live_link%" /al 2>nul | findstr /i "Green" >nul
if !errorlevel!==0 (
set "green_status=LIVE"
) else (
set "blue_status=????"
set "green_status=????"
)
)
) else (
echo [WARNING] Symlink does not exist: %live_link%
echo.
)
)

echo BLUE: !blue_status!
echo GREEN: !green_status!
echo.
echo Symlink: %live_link%
if exist "%live_link%" (
dir "%live_link%" /al 2>nul | findstr /i "Production"
) else (
echo (not found^)
)

echo.

:: Show version info if available
if exist "%blue_dir%\build_info.txt" (
echo Blue version:
type "%blue_dir%\build_info.txt"
echo.
)
if exist "%green_dir%\build_info.txt" (
echo Green version:
type "%green_dir%\build_info.txt"
echo.
)

pause

Common Mistakes

The Wrong Way: Deploying Over the Live Environment

:: WRONG - Updates files while users are actively using them
robocopy "C:\Build\Output" "C:\WebApps\Production" /mir

Output Concern: Deploying directly over the live environment causes downtime while files are being copied. If the deployment fails midway, the application is left in a partially updated, broken state. Blue-green deployment avoids this by always deploying to the idle environment.

The Wrong Way: Not Verifying Before Switching

:: WRONG - Switches traffic without testing the new deployment
mklink /d Production Green
:: If Green has a broken config, all users see errors

Always run health checks, smoke tests, or at least verify the application starts correctly before switching traffic to the new environment.

Best Practices

  1. Always deploy to the idle environment: Never modify the live environment directly.
  2. Verify before switching: Run health checks, smoke tests, or manual verification.
  3. Keep the rollback path instant: The previous environment remains untouched, enabling sub-second rollback.
  4. Include version info: Store build_info.txt in each environment for easy identification.
  5. Automate the full cycle: Combine deploy, verify, and switch into a single workflow script.

Conclusion

Blue-green deployment switchover in Batch Script is implemented through directory symbolic links or IIS site start/stop commands. The pattern ensures zero-downtime deployments by always deploying to the idle environment and switching traffic only after verification. The most powerful aspect is the instant rollback capability: if the new version has issues, switching back to the previous environment takes less than a second. By combining this strategy with health checks, version tracking, and Robocopy-based deployments, teams achieve professional-grade release management from simple Batch scripts.