Manual Deployment
Manual Deployment
At first, deployment is done manually.
These instructions show how to deploy the backend and frontend without an automated pipeline. Later in the course, we will revisit this process and automate it with a stronger focus on security, repeatability, and CI/CD.
The manual deployment flow is useful because it helps you understand what a deployment pipeline eventually needs to do for you.
1. Prerequisites
Before you start, make sure the infrastructure has already been deployed with Terraform.
Go to the Terraform folder:
cd infrastructure/day-1Run:
terraform outputWrite down the following values:
backend_app_name=app-cloudsec-mmommers-dev-api
backend_app_url=https://app-cloudsec-mmommers-dev-api.azurewebsites.net
frontend_storage_account_name=stmmommersdevrdjq48
frontend_static_website_url=https://stmmommersdevrdjq48.z6.web.core.windows.net/
resource_group_name=rg-cloudsec-mmommers-devYou will need these values in the next steps.
For the examples below, replace:
<RESOURCE_GROUP_NAME>
<BACKEND_APP_NAME>
<FRONTEND_STORAGE_ACCOUNT_NAME>with the values from your own Terraform output.
2. Deploy the backend
Navigate to the backend folder:
cd ../../course-cloud-security-app/apps/backendInstall dependencies if needed:
npm installBuild the backend:
npm run buildThe backend should now have a dist folder.
3. Create the backend deployment zip
Open Git Bash.
Warning
On Windows, avoid using PowerShell Compress-Archive for this deployment zip. It can create zip files with path separator issues that may cause problems during Azure App Service deployment.
Use the zip command from Git Bash, GoW, WSL, or another Linux-compatible shell instead.
Navigate to the backend folder again if needed:
cd course-cloud-security-app/apps/backendFor now, we let Azure App Service build or finalize the app during deployment. Later in the course, we will build proper deployment artifacts in CI/CD.
Create the zip file:
zip -r backend.zip `
src package.json package-lock.json tsconfig.jsonPowerShell line endings
The examples in this course often use PowerShell backticks for line continuation. In Bash or Git Bash, use a backslash instead.
4. Upload the backend to Azure App Service
Deploy the zip file to the backend App Service:
az webapp deploy `
--resource-group "<RESOURCE_GROUP_NAME>" `
--name "<BACKEND_APP_NAME>" `
--src-path backend.zip `
--type zipAzure CLI reference:
https://learn.microsoft.com/en-us/cli/azure/webapp?view=azure-cli-latest#az-webapp-deploy
5. Configure the backend startup command
If Azure complains about the startup file, or if the app does not start correctly, configure the startup command manually:
az webapp config set `
--resource-group "<RESOURCE_GROUP_NAME>" `
--name "<BACKEND_APP_NAME>" `
--startup-file "node dist/index.js"This tells Azure App Service how to start the Node.js backend.
6. Test the backend
Test the health endpoint:
curl https://<BACKEND_APP_NAME>.azurewebsites.net/api/healthExpected result:
The backend should return a successful health response.If this fails, check:
az webapp log tail `
--resource-group "<RESOURCE_GROUP_NAME>" `
--name "<BACKEND_APP_NAME>"Common issues are:
Wrong startup command
Missing package.json
Missing build output
Incorrect zip structure
App Service still restarting
Environment variables missingDeploy the frontend
The frontend is hosted as a static website in an Azure Storage Account.
Before building the frontend, you need to configure it with the correct backend API URL.
7. Build the frontend
Navigate to the frontend folder:
cd ../frontendSet the backend API URL.
In PowerShell:
$env:VITE_API_BASE_URL="https://<BACKEND_APP_NAME>.azurewebsites.net"
npm run buildIn Bash or Git Bash:
export VITE_API_BASE_URL="https://<BACKEND_APP_NAME>.azurewebsites.net"
npm run buildThis creates a production build in the dist folder.
Tips
The VITE_API_BASE_URL value is embedded into the frontend during build time. If you change the backend URL later, you need to rebuild and redeploy the frontend.
8. Upload the frontend to Azure Storage
Deploy the frontend files to the static website container of the Storage Account:
az storage blob upload-batch `
--account-name "<FRONTEND_STORAGE_ACCOUNT_NAME>" `
--destination '$web' `
--source dist `
--overwriteThe special destination container $web is used by Azure Storage static website hosting.
9. Test the frontend
Open the frontend URL from your Terraform output:
https://<FRONTEND_STATIC_WEBSITE_URL>Or check the value again:
terraform output frontend_static_website_urlVerify that:
[ ] The frontend loads in the browser.
[ ] The frontend can call the backend API.
[ ] The backend health endpoint works.
[ ] The browser console does not show CORS errors.10. Troubleshooting
Backend deployment succeeds, but the app does not start
Check the startup command:
az webapp config show `
--resource-group "<RESOURCE_GROUP_NAME>" `
--name "<BACKEND_APP_NAME>" `
--query linuxFxVersionSet the startup command again if needed:
az webapp config set `
--resource-group "<RESOURCE_GROUP_NAME>" `
--name "<BACKEND_APP_NAME>" `
--startup-file "node dist/index.js"Then inspect the logs:
az webapp log tail `
--resource-group "<RESOURCE_GROUP_NAME>" `
--name "<BACKEND_APP_NAME>"Backend zip deployment fails
Make sure you created the zip from the backend folder and that it contains the expected files:
src/
package.json
package-lock.json
tsconfig.jsonAvoid zipping the parent folder itself. The files should be at the root of the zip.
Frontend cannot reach the backend
Check that the frontend was built with the correct API URL:
VITE_API_BASE_URL=https://<BACKEND_APP_NAME>.azurewebsites.netIf the value was wrong, rebuild and redeploy the frontend.
Browser shows a CORS error
The backend must allow requests from the frontend static website URL.
Check the frontend URL:
terraform output frontend_static_website_urlThen make sure the backend has the correct CORS configuration or environment variable, depending on how the backend is implemented.
11. Manual deployment checklist
Before continuing, verify the following:
[ ] I have the Terraform output values.
[ ] I know the resource group name.
[ ] I know the backend App Service name.
[ ] I know the frontend Storage Account name.
[ ] I built the backend.
[ ] I created the backend zip with Git Bash, GoW, WSL, or another Linux-compatible zip tool.
[ ] I deployed the backend zip to Azure App Service.
[ ] I configured the backend startup command if needed.
[ ] I tested the backend health endpoint.
[ ] I built the frontend with the correct VITE_API_BASE_URL.
[ ] I uploaded the frontend dist folder to the $web container.
[ ] I opened the frontend static website URL.
[ ] The frontend can communicate with the backend.12. Why we start manually
Manual deployment is not the final goal.
We start manually so you can see which steps are involved:
Build backend
Package backend
Deploy backend
Configure startup
Build frontend with backend URL
Upload frontend files
Test the complete applicationLater, these steps will be moved into a more secure and repeatable deployment process using automation and CI/CD.