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 Architecture • High-Level Overview • Azure Resources • Access & Security • Repository Structure • Backend API • UI Behavior • Data Model • Deployment Pipeline • Local Development • Use Cases • Planned Improvements • TLDR
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-UsersStatusBoard-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:
- Install requirements
- Compile-check Flask app
- Bundle repository
- 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.
FeedbackAdd something useful. Ask good questions. Help someone else learn.