At this yearβs Orlando Code Camp, I walked through how to go from a basic Flask app on your laptop to a live deployment in Azure β using VS Code, the Azure CLI, Bicep, and GitHub Actions.
ποΈ You can find the full demo code here: github.com/estebang/TaskTracker
The idea was to show how you can move fast, stay in your dev environment, and automate just enough to keep things clean β all without introducing too much complexity.
What We Built: A Simple Task Tracker
To keep the demo realistic but lightweight, I built a Flask-based task tracker.
- Add / complete / delete tasks
- In-memory storage (no DB needed)
- Clean UI using HTML + CSS
- Runs locally, deploys to Azure
Project structure:
task-tracker/
βββ app.py
βββ requirements.txt
βββ templates/
β βββ index.html
βββ static/
β βββ styles.css
Run It Locally First
Install the dependencies:
pip install flask gunicorn
pip freeze > requirements.txt
Then run it:
python app.py
Open your browser to http://localhost:5000
.
Set It Up for Azure
Before we ship it to the cloud, we need to give Azure a few things:
- A
requirements.txt
file - A startup command for running Flask with Gunicorn
- A zipped version of the app
Startup command:
gunicorn --bind=0.0.0.0 --timeout 600 app:app
Deploying from the VS Code Terminal (Azure CLI)
1. Login to Azure
az login
2. Set some variables
APP_NAME="tasktracker-$RANDOM"
RESOURCE_GROUP="tasktracker-rg"
PLAN_NAME="tasktracker-plan"
LOCATION="eastus"
3. Create Azure resources
az group create --name $RESOURCE_GROUP --location $LOCATION
az appservice plan create \
--name $PLAN_NAME \
--resource-group $RESOURCE_GROUP \
--sku B1 \
--is-linux
az webapp create \
--resource-group $RESOURCE_GROUP \
--plan $PLAN_NAME \
--name $APP_NAME \
--runtime "PYTHON:3.10"
4. Set the startup command
az webapp config set \
--resource-group $RESOURCE_GROUP \
--name $APP_NAME \
--startup-file "gunicorn --bind=0.0.0.0 --timeout 600 app:app"
5. Zip the app (PowerShell)
Compress-Archive -Path * -DestinationPath app.zip -Force -Exclude venv, __pycache__
6. Deploy the zip
az webapp deployment source config-zip \
--resource-group $RESOURCE_GROUP \
--name $APP_NAME \
--src app.zip
7. Done
echo "https://$APP_NAME.azurewebsites.net"
Automating with GitHub Actions and Bicep
After getting things running manually, I showed how to automate it using GitHub Actions and Bicep.
I used GitHub Copilot to scaffold both the Bicep template and GitHub Actions workflow. You just type something like:
// Create an App Service Plan and Linux Web App for Python
β¦and it gives you a solid starting point.
Bicep Template (infra/main.bicep
)
param appName string
param location string = 'eastus'
resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = {
name: '${appName}-plan'
location: location
sku: {
name: 'B1'
tier: 'Basic'
}
kind: 'linux'
properties: {
reserved: true
}
}
resource webApp 'Microsoft.Web/sites@2022-03-01' = {
name: appName
location: location
kind: 'app,linux'
properties: {
serverFarmId: appServicePlan.id
siteConfig: {
linuxFxVersion: 'PYTHON|3.10'
appCommandLine: 'gunicorn --bind=0.0.0.0 --timeout 600 app:app'
}
}
}
GitHub Actions Workflow
.github/workflows/deploy.yml
:
name: Deploy Flask App to Azure
on:
push:
branches: [main]
env:
AZURE_WEBAPP_NAME: tasktracker-dev
AZURE_RESOURCE_GROUP: tasktracker-rg
AZURE_REGION: eastus
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Log in to Azure
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Deploy infrastructure using Bicep
uses: azure/cli@v1
with:
inlineScript: |
az deployment group create \
--resource-group ${{ env.AZURE_RESOURCE_GROUP }} \
--template-file infra/main.bicep \
--parameters appName=${{ env.AZURE_WEBAPP_NAME }} location=${{ env.AZURE_REGION }}
- name: Zip and deploy to App Service
run: |
zip -r app.zip . -x "venv/*" "__pycache__/*"
az webapp deployment source config-zip \
--resource-group ${{ env.AZURE_RESOURCE_GROUP }} \
--name ${{ env.AZURE_WEBAPP_NAME }} \
--src app.zip
Set up GitHub Secret
az ad sp create-for-rbac --name "flask-github-action" \
--role contributor \
--scopes /subscriptions/<your-subscription-id> \
--sdk-auth
Copy the output JSON and store it as a secret called AZURE_CREDENTIALS
.
Thatβs a Wrap
So we went from:
- Flask app running locally
- CLI deployment to Azure
- IaC with Bicep
- GitHub Actions for CI/CD
- Copilot for code generation
Demo code: github.com/estebang/TaskTracker
Thanks for coming to my talk!