Skip to content

GitHub Status Board (GitHub + Azure App Service)

Estimated Time: 20–40 min  Skill Level: Intermediate  Goal: Build a secure, lightweight operational dashboard

A walkthrough for deploying a small, authenticated, real-time status board using GitHub, Azure App Service, Flask, and Entra ID.
The board supports infrastructure visibility, service health, and optional presence indicators — all backed by a single JSON file.

Use the links below to jump directly to the section you need:

Why This ArchitectureHigh-Level OverviewAzure ResourcesAccess & SecurityRepository StructureBackend APIUI BehaviorData ModelDeployment PipelineLocal DevelopmentUse CasesPlanned ImprovementsTLDR


Why This Architecture

I could have built this entirely with Azure-native services, or as a static GitHub Pages front-end, or using a low-code tool.

But this design fit how I already work:

  • GitHub is my source of truth
  • Azure App Service is a simple, inexpensive runtime
  • Entra ID gives me authentication and Conditional Access
  • JSON keeps the state layer small and predictable
  • Flask lets me patch and extend quickly
  • GitHub Actions deploys automatically
  • Everything is auditable and versioned

The goal was to build a lightweight operational dashboard for:

  • personal infrastructure
  • cloud VMs
  • GitHub service uptime
  • lab environments
  • event or workshop visibility
  • shared project status
  • optional presence indicators (active, inactive, away)

This design stays simple, secure, and flexible enough to grow over time.


High-Level Overview

The Status Board is a Python/Flask app deployed to Azure App Service and protected with Entra ID.

Components

Component Description
Flask API Serves data and handles updates
HTML/CSS/JS Front-end dashboard
board.json State engine
Azure App Service Runtime
Entra ID Authentication
Conditional Access MFA + location rules
IP Restrictions Network boundary
GitHub Actions Deployments

Two Modules

Presence (Optional)

  • active
  • inactive
  • away
  • logical location (Cloud-West, Cloud-East, HomeLab-1)

Infrastructure

  • VM or service state
  • home lab nodes
  • GitHub Actions or Pages uptime
  • Cloud region indicators

Azure Resources

Example Azure footprint:

  • Resource Group: statusboard-rg
  • App Service Plan: statusboard-asp
  • App Service: app-statusboard

Key Configuration

  • Runtime: Python 3.11
  • Startup command:
    python api/app.py
    
  • HTTPS-only
  • App Service Authentication enabled (Easy Auth)

DNS Example

status.andrewzamora.io → <app>.azurewebsites.net

Access & Security

Authentication: Entra ID

Configured with Easy Auth:

  • App Registration: Status Board
  • Enterprise App: created from registration
  • User assignment required

Groups allowed:

  • StatusBoard-Users
  • StatusBoard-Admins

Conditional Access

  • Cloud app: Status Board
  • Users: the two groups above
  • Location: US only
  • Grant: require MFA (Standard Sign-In)

IP Restrictions

Configured at the App Service level:

  • Allow office IP
  • Allow VPN
  • Optional: home IP
  • Deny all other traffic

Repository & File Structure

statusboard/
├── api/
│   ├── app.py
│   ├── board.json
│   ├── templates/
│   │   └── index.html
│   └── static/
│       ├── script.js
│       ├── style.css
│       └── Logo.png
├── .github/
│   └── workflows/
│       └── deploy.yml
├── requirements.txt
└── README.md

Backend API Overview

GET /

Serves the dashboard interface.

GET /api/board

Returns full board state:

{
  "locations": ["Cloud-West", "Cloud-East", "HomeLab-1"],
  "people": [
    {
      "id": "andy",
      "name": "Andy",
      "github": "d3fnd",
      "location": "Cloud-West",
      "status": "active",
      "updated": "2025-01-12T18:05:23Z"
    }
  ],
  "infrastructure": [
    {
      "id": "vm-lab01",
      "name": "HomeLab VM 01",
      "status": "running",
      "updated": "2025-01-12T17:22:11Z"
    }
  ]
}

POST /api/board/

Updates a single entry:

{
  "status": "inactive",
  "location": "HomeLab-1"
}

Writes use a temp-file → replace pattern to avoid corruption.


Frontend & UI Behavior

  • Two tabs: People (default) + Infrastructure
  • Color-accented tiles for status
  • Location filter
  • Sort by name or updated
  • GitHub avatar lookup
  • Responsive mobile layout
  • Intro modal stored in localStorage

Data Model & Storage

Example person:

{
  "id": "andy",
  "name": "Andy",
  "github": "d3fnd",
  "location": "Cloud-West",
  "status": "active",
  "updated": "2025-01-10T16:45:00Z"
}

Example infrastructure:

{
  "id": "vm-01",
  "name": "Lab VM 01",
  "status": "running",
  "updated": "2025-01-10T14:15:00Z"
}

Deployment Pipeline

GitHub Actions deploy on push to main.

Steps:

  1. Install requirements
  2. Compile-check Flask app
  3. Bundle repository
  4. Deploy using:
    AZURE_WEBAPP_PUBLISH_PROFILE
    

If deployment fails:

  • Check workflow logs
  • Confirm Python 3.11
  • Validate requirements.txt
  • Rotate publish profile

Local Development

cd api
python -m venv .venv
source .venv/bin/activate
pip install -r ../requirements.txt
python app.py

Dashboard available at:

http://localhost:5000

Use Cases

Infrastructure Visibility

  • home lab VM status
  • service uptime
  • GitHub Actions or Pages health
  • Azure resource readiness
  • region indicators
  • sandbox or test environment availability

Presence (Optional)

  • active
  • inactive
  • away
  • Cloud-West / Cloud-East / HomeLab-1

Good for:

  • collaborative work
  • projects
  • events
  • workshops
  • lab coordination

Planned Improvements

  • per-user update enforcement
  • updated_by attribution
  • structured logging
  • infrastructure polling (REST, ICMP, API)
  • multiple board views
  • webhook integrations

TLDR

GitHub = source of truth
Azure App Service = runtime
Entra ID = authentication
Conditional Access = MFA + rules
App Service IP restrictions = second boundary
Flask = backend
board.json = state engine
HTML/CSS/JS = UI
GitHub Actions = deployments

Use it for infrastructure, labs, uptime, events, or presence.

Join the Discussion

Got a question, idea, or a better way to do it? Drop it below — I read every comment and update guides based on real-world feedback.

Add something useful. Ask good questions. Help someone else learn.