This page documents the current dev-side asset deployment script that uploads files to an S3 bucket and then invalidates a selected CloudFront distribution. The script is interactive and is intended for operator-driven publishing rather than unattended release automation.
Before running the script, the local machine must already be able to execute AWS CLI commands successfully.
| Requirement | Purpose |
|---|---|
aws s3 ls |
Lists available buckets for the operator to choose from. |
aws cloudfront list-distributions |
Lists distributions so the operator can choose the target invalidation. |
| AWS credentials already configured | The script assumes AWS CLI authentication is already working. |
| Correct working directory | aws s3 sync . ... uploads from the current folder, so the script must be run from the directory containing the files to publish. |
The script begins with two interactive selections:
It runs aws s3 ls, numbers the available buckets, and stores the selected bucket as S3_BUCKET.
It runs aws cloudfront list-distributions and shows the distribution ID, domain name, and comment for each available entry.
The operator can proceed, re-choose the bucket/distribution pair, or exit before any destructive operation starts.
The current UI-side mapping used for bucket to CloudFront pairing is:
| S3 bucket | CloudFront distribution ID |
|---|---|
uat-benefits-app-bucket |
EUBZ8CDSV9KZZ |
uat-hr-app-bucket |
E9TNPRI9ITM1M |
benefits-app-bucket |
E1MKRK4U5MZ3BD |
live-hr-app-bucket |
E3TE01DPKHTD8B |
These pairs are aligned with the bucket-to-distribution mapping currently used
in app/Views/fedeploy.php.
Once confirmed, the script executes five sequential steps:
| Step | What it does |
|---|---|
1/5 |
Deletes the existing contents of the selected S3 bucket with aws s3 rm --recursive. |
2/5 |
Syncs the current directory into the bucket with aws s3 sync, excluding *.bat and *.bat.*. |
3/5 |
Creates a CloudFront invalidation for /* and captures the invalidation ID. |
4/5 |
Waits 45 seconds before the first status check. |
5/5 |
Polls invalidation status until it becomes Completed or retries are exhausted. |
aws s3 rm "%S3_BUCKET%" --recursive
aws s3 sync . "%S3_BUCKET%" --exclude "*.bat" --exclude "*.bat.*"
aws cloudfront create-invalidation --distribution-id %DIST_ID% --paths "/*"
The script includes explicit status handling for CloudFront invalidation:
| Setting | Value | Purpose |
|---|---|---|
WAIT_SECONDS |
45 |
Initial wait before the first invalidation status check. |
MAX_STATUS_RETRIES |
20 |
Upper bound for repeated status polling attempts. |
| Retry delay | 15 seconds |
Pause between repeated invalidation status checks. |
If AWS returns an error or the status is empty, the script retries until the
maximum retry count is reached. If the returned status becomes
Completed, the deployment is treated as successful.
Completed
InProgress
The script syncs the current directory, so it should be started only from the folder whose contents should go to S3.
The confirmation step exists to prevent publishing to the wrong S3 bucket or invalidating the wrong distribution.
The script explicitly excludes batch files during sync so deployment helpers are not uploaded into the target bucket.
The script does not finish immediately after creating the invalidation; it waits and polls until the status is complete or retries are exhausted.
Full reference copy of the current Windows batch script:
@echo off
setlocal EnableDelayedExpansion
REM ==============================
REM CONFIGURATION
REM ==============================
set WAIT_SECONDS=45
set MAX_STATUS_RETRIES=20
REM ==============================
REM SELECT S3 BUCKET
REM ==============================
:SELECT_BUCKET
cls
echo =========================================
echo AVAILABLE S3 BUCKETS
echo =========================================
echo.
set count=0
for /f "tokens=3 delims= " %%a in ('aws s3 ls') do (
set /a count+=1
set bucket[!count!]=%%a
echo !count!. %%a
)
if %count%==0 (
echo [ERROR] No S3 buckets found.
pause
exit /b 1
)
echo.
set /p bucketChoice=Select bucket number:
if not defined bucket[%bucketChoice%] (
echo [ERROR] Invalid selection.
ping -n 3 127.0.0.1 >nul
goto SELECT_BUCKET
)
set S3_BUCKET=s3://!bucket[%bucketChoice%]!
REM ==============================
REM SELECT CLOUDFRONT DISTRIBUTION
REM ==============================
:SELECT_CF
cls
echo =========================================
echo AVAILABLE CLOUDFRONT DISTRIBUTIONS
echo =========================================
echo.
set cfcount=0
for /f "tokens=1,2,3 delims= " %%a in ('aws cloudfront list-distributions --query "DistributionList.Items[*].[Id,DomainName,Comment]" --output text') do (
set /a cfcount+=1
set cfid[!cfcount!]=%%a
echo !cfcount!. ID: %%a
echo Domain: %%b
echo Desc : %%c
echo.
)
if %cfcount%==0 (
echo [ERROR] No CloudFront distributions found.
pause
exit /b 1
)
set /p cfChoice=Select CloudFront number:
if not defined cfid[%cfChoice%] (
echo [ERROR] Invalid selection.
ping -n 3 127.0.0.1 >nul
goto SELECT_CF
)
set DIST_ID=!cfid[%cfChoice%]!
REM ==============================
REM CONFIRM SELECTION
REM ==============================
:CONFIRM
cls
echo =========================================
echo CONFIRM YOUR SELECTION
echo =========================================
echo.
echo S3 Bucket : %S3_BUCKET%
echo CloudFront : %DIST_ID%
echo.
echo 1. Proceed
echo 2. Re-choose
echo 3. Exit
echo.
set /p confirmChoice=Enter choice:
if "%confirmChoice%"=="1" goto DEPLOY
if "%confirmChoice%"=="2" goto SELECT_BUCKET
if "%confirmChoice%"=="3" exit /b 0
goto CONFIRM
REM ==============================
REM DEPLOYMENT FLOW
REM ==============================
:DEPLOY
cls
echo =========================================
echo DEPLOYMENT STARTED
echo =========================================
echo.
REM --------------------------------------------------
REM STEP 1: REMOVE EXISTING S3 FILES
REM --------------------------------------------------
echo [1/5] Removing existing files from %S3_BUCKET%...
aws s3 rm "%S3_BUCKET%" --recursive
if %ERRORLEVEL% NEQ 0 (
echo [ERROR] S3 DELETE FAILED. Aborting.
exit /b 1
)
echo [OK] S3 cleanup completed.
echo.
REM --------------------------------------------------
REM STEP 2: SYNC NEW FILES
REM FIX B1+B2: Quoted S3_BUCKET, exclude .bat files from sync
REM --------------------------------------------------
echo [2/5] Uploading new files to %S3_BUCKET%...
aws s3 sync . "%S3_BUCKET%" --exclude "*.bat" --exclude "*.bat.*"
if %ERRORLEVEL% NEQ 0 (
echo [ERROR] S3 SYNC FAILED. Aborting.
exit /b 1
)
echo [OK] S3 sync completed.
echo.
REM --------------------------------------------------
REM STEP 3: CREATE CLOUDFRONT INVALIDATION
REM FIX B3: Pre-clear variable before reading temp file
REM FIX B4: Use for /f to read temp file -- strips \r automatically
REM --------------------------------------------------
echo [3/5] Creating CloudFront invalidation...
set INVALIDATION_ID=
aws cloudfront create-invalidation --distribution-id %DIST_ID% --paths "/*" --query "Invalidation.Id" --output text > "%TEMP%\cf_inv_id.txt" 2>"%TEMP%\cf_inv_err.txt"
if %ERRORLEVEL% NEQ 0 (
echo [ERROR] CloudFront invalidation creation failed.
echo --- AWS Error Output ---
type "%TEMP%\cf_inv_err.txt"
del "%TEMP%\cf_inv_id.txt" >nul 2>&1
del "%TEMP%\cf_inv_err.txt" >nul 2>&1
exit /b 1
)
REM for /f auto-strips \r\n giving a clean value -- fixes the critical \r bug
for /f "usebackq delims=" %%i in ("%TEMP%\cf_inv_id.txt") do set INVALIDATION_ID=%%i
del "%TEMP%\cf_inv_id.txt" >nul 2>&1
del "%TEMP%\cf_inv_err.txt" >nul 2>&1
if "%INVALIDATION_ID%"=="" (
echo [ERROR] Invalidation ID was empty after creation. Aborting.
exit /b 1
)
echo [OK] Invalidation created.
echo Invalidation ID : %INVALIDATION_ID%
echo Distribution ID : %DIST_ID%
echo.
REM --------------------------------------------------
REM STEP 4: WAIT 45 SECONDS
REM --------------------------------------------------
set /a PING_COUNT=%WAIT_SECONDS%+1
echo [4/5] Waiting %WAIT_SECONDS% seconds before polling...
ping -n %PING_COUNT% 127.0.0.1 >nul
echo [OK] Wait complete.
echo.
REM --------------------------------------------------
REM STEP 5: POLL INVALIDATION STATUS
REM FIX B5: for /f strips \r so STATUS matches "Completed" correctly
REM FIX B6: Retry counter prevents infinite loop on AWS errors
REM --------------------------------------------------
set retryCount=0
:CHECK_STATUS
cls
echo =========================================
echo Checking CloudFront Invalidation Status
echo =========================================
echo.
echo Invalidation ID : %INVALIDATION_ID%
echo Distribution ID : %DIST_ID%
echo.
set STATUS=
aws cloudfront get-invalidation --distribution-id %DIST_ID% --id %INVALIDATION_ID% --query "Invalidation.Status" --output text > "%TEMP%\cf_status.txt" 2>"%TEMP%\cf_status_err.txt"
if %ERRORLEVEL% NEQ 0 (
echo [WARN] AWS call failed. Error output:
type "%TEMP%\cf_status_err.txt"
del "%TEMP%\cf_status.txt" >nul 2>&1
del "%TEMP%\cf_status_err.txt" >nul 2>&1
set /a retryCount+=1
if !retryCount! GEQ %MAX_STATUS_RETRIES% (
echo [ERROR] Max retries ^(%MAX_STATUS_RETRIES%^) reached. Deployment status unknown.
exit /b 1
)
echo Retrying in 15 seconds... [Attempt !retryCount!/%MAX_STATUS_RETRIES%]
ping -n 16 127.0.0.1 >nul
goto CHECK_STATUS
)
for /f "usebackq delims=" %%s in ("%TEMP%\cf_status.txt") do set STATUS=%%s
del "%TEMP%\cf_status.txt" >nul 2>&1
del "%TEMP%\cf_status_err.txt" >nul 2>&1
if "%STATUS%"=="" (
set /a retryCount+=1
echo [WARN] Empty status received.
if !retryCount! GEQ %MAX_STATUS_RETRIES% (
echo [ERROR] Max retries ^(%MAX_STATUS_RETRIES%^) reached. Aborting.
exit /b 1
)
echo Retrying in 15 seconds... [Attempt !retryCount!/%MAX_STATUS_RETRIES%]
ping -n 16 127.0.0.1 >nul
goto CHECK_STATUS
)
echo Current Status : %STATUS%
echo.
if /I "%STATUS%"=="Completed" (
echo =========================================
echo INVALIDATION COMPLETED SUCCESSFULLY
echo =========================================
goto END_SCRIPT
)
set /a retryCount+=1
if !retryCount! GEQ %MAX_STATUS_RETRIES% (
echo [ERROR] Max retries ^(%MAX_STATUS_RETRIES%^) reached. Last status: %STATUS%
exit /b 1
)
echo [INFO] Still "%STATUS%". Checking again in 15 seconds... [Attempt !retryCount!/%MAX_STATUS_RETRIES%]
ping -n 16 127.0.0.1 >nul
goto CHECK_STATUS
REM ==============================
REM DONE
REM ==============================
:END_SCRIPT
echo.
echo =========================================
echo DEPLOYMENT FINISHED SUCCESSFULLY
echo =========================================
echo.
pause
exit /b 0