Azure — NHS Quickstart
Managed TLS-by-default hosting close to M365/Entra ID. Great for APIs (FastAPI/Express), dashboards (React/Dash/Shiny), and Azure SQL when you want a managed SQL Server engine. Use Key Vault for secrets and Managed Identity so apps run without passwords.
Great for: Developer · Data Engineer (services & pipelines) · BI (thin UI).
⚙️ 10‑minute “hello” deploy (App Service — container)
Goal: containerise a tiny API and deploy to an HTTPS URL (lock down later).
0) Minimal API + Dockerfile
- FastAPI (Python)
- Express (Node.js)
from fastapi import FastAPI
app = FastAPI()
@app.get("/health")
def health(): return {"status":"ok"}
@app.get("/hello")
def hello(): return {"message":"Hello, NHS!"}
FROM python:3.11-slim
WORKDIR /app
COPY service/main.py ./main.py
RUN pip install fastapi uvicorn[standard]
EXPOSE 8000
CMD ["uvicorn","main:app","--host","0.0.0.0","--port","8000"]
import express from "express";
const app = express();
app.get("/health", (_req,res)=>res.json({status:"ok"}));
app.get("/hello", (_req,res)=>res.json({message:"Hello, NHS!"}));
app.listen(8000, ()=>console.log("Running on :8000"));
{
"name": "nhs-hello",
"type": "module",
"dependencies": { "express": "^4.19.2" },
"scripts": { "start": "node server.js" }
}
FROM node:20-alpine
WORKDIR /app
COPY service/package.json service/server.js ./
RUN npm ci --only=production || npm i --only=production
EXPOSE 8000
CMD ["node","server.js"]
1) Create ACR and push
# variables
RG=rg-nhs
REGION=uksouth
ACR=nhsregistry
IMAGE=nhs-hello-api
az group create -n $RG -l $REGION
az acr create -g $RG -n $ACR --sku Basic
az acr login -n $ACR
# build & push
docker build -t $IMAGE ./
docker tag $IMAGE:latest $ACR.azurecr.io/$IMAGE:latest
docker push $ACR.azurecr.io/$IMAGE:latest
2) App Service (Linux, container)
PLAN=nhs-plan
APP=nhs-hello-api
az appservice plan create -g $RG -n $PLAN --is-linux --sku B1
az webapp create -g $RG -p $PLAN -n $APP --deployment-container-image-name $ACR.azurecr.io/$IMAGE:latest
# configure container port and health check
az webapp config appsettings set -g $RG -n $APP --settings WEBSITES_PORT=8000
az webapp config set -g $RG -n $APP --health-check-path /health
You’ll get an HTTPS URL like: https://<app-name>.azurewebsites.net/hello.
Lock down later: Private networking (VNET integration), Front Door + WAF, and IP allow‑lists. For private DB access, use Private Endpoints.
🗃️ Azure SQL (managed SQL Server)
SQLSERVER=nhs-sql-srv
SQLDB=NHS_Analytics
az sql server create -g $RG -n $SQLSERVER -u svc_ds -p "ChangeMe_123!"
az sql db create -g $RG -s $SQLSERVER -n $SQLDB --service-objective S0
Recommended: use Managed Identity + contained users (no passwords).
# enable system-assigned identity on the web app
az webapp identity assign -g $RG -n $APP
APP_OBJECT_ID=$(az webapp identity show -g $RG -n $APP --query principalId -o tsv)
echo $APP_OBJECT_ID
Connect to Azure SQL as an Azure AD admin, then in the database run:
-- Create contained AAD user mapped to the app's managed identity
CREATE USER [nhs-hello-api] FROM EXTERNAL PROVIDER;
ALTER ROLE db_datareader ADD MEMBER [nhs-hello-api];
-- add db_datawriter only if needed
Use your actual app name in square brackets. If names collide, use the identity’s object ID notation supported by your tooling.
🔑 Key Vault for secrets
KV=nhs-secrets
az keyvault create -g $RG -n $KV
az keyvault secret set --vault-name $KV -n SqlPassword --value "ChangeMe_123!"
# grant the web app's managed identity access to secrets
az keyvault set-policy -n $KV --object-id $APP_OBJECT_ID --secret-permissions get list
At runtime, retrieve secrets via SDKs (Python: azure-identity, azure-keyvault-secrets; Node: @azure/identity, @azure/keyvault-secrets).
For App Service, you can also use Key Vault references in app settings:
@Microsoft.KeyVault(SecretUri=https://nhs-secrets.vault.azure.net/secrets/SqlPassword/)
🚀 Azure Container Apps (serverless containers)
If you prefer Kubernetes-like flexibility without running AKS:
ENV=nhs-env
az containerapp env create -g $RG -n $ENV -l $REGION
az containerapp create -g $RG -n nhs-hello-api --environment $ENV --image $ACR.azurecr.io/$IMAGE:latest --ingress external --target-port 8000 --registry-server $ACR.azurecr.io --registry-username $(az acr credential show -n $ACR --query username -o tsv) --registry-password $(az acr credential show -n $ACR --query passwords[0].value -o tsv)
Container Apps supports scale to zero, VNETs, secrets, and Dapr (optional).