Orlando Code Camp: Automate Azure Deployment with GitHub Actions and Bicep

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!

Scroll to Top