Your organization's finances, under your control.
OpenTreasury is an open-source tool that helps non-profit organizations manage bank transactions, categorize spending, and produce financial reports. It runs in your own Azure subscription, so your financial data stays yours. This guide walks you through deploying it — no programming required.
Before you start, make sure you have the following. The entire setup takes about an hour for someone doing it for the first time.
| Prerequisite | What it means | How to check |
|---|---|---|
| Azure subscription | A pay-as-you-go Azure account. Free trial or Visual Studio subscriptions also work. | Go to portal.azure.com and sign in. If you see a dashboard, you have one. |
| Entra ID admin access | Permission to create app registrations in your organization's Microsoft Entra ID (formerly Azure Active Directory). | Ask your IT admin: "Can I create app registrations in our Azure AD?" |
| GitHub account | A free GitHub account to host your deployment repository. | Go to github.com and sign in. |
| Azure CLI | A command-line tool for managing Azure resources. | See How to install the Azure CLI below. |
| Git | Version control tool to download the project files. | Open a terminal and run git --version. If it prints a version number, you're set. |
No programming required. You don't need to know Python, JavaScript, or any programming language. You'll edit a few configuration values and run a script — that's it.
Windows (PowerShell):
winget install Microsoft.AzureCLImacOS:
brew install azure-cliLinux (Ubuntu/Debian):
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bashAfter installing, verify it works:
az --version
You should see a version number (2.x or later).
The gh command-line tool makes creating repositories easier, but it's optional — you can also create repos through the GitHub website.
Windows (PowerShell):
winget install GitHub.climacOS:
brew install ghLinux:
sudo apt install ghOpenTreasury is designed to be affordable for small organizations. Here's what it costs to run:
| Resource | What it does | Monthly cost (estimate) |
|---|---|---|
| App Service (B1) | Runs the backend API | ~€12 |
| Cosmos DB (Serverless) | Stores your transaction data | ~€1–5 (depends on usage) |
| Static Web App (Free) | Hosts the web interface | €0 |
| Key Vault | Stores configuration secrets securely | < €1 |
| Application Insights | Monitors application health | < €1 |
| Total | ~€15–25/month |
These costs depend on your Azure region and usage. A 20-person NGO with a few hundred transactions per month will typically be at the lower end.
Tip: You can reduce costs further by enabling Cosmos DB Free Tier (one free account per Azure subscription — ask your IT admin if it's already in use).
Your deployment repository is a private GitHub repo that holds your organization's deployment configuration. It's separate from the OpenTreasury product code, so your configuration stays private.
Option A: Using GitHub CLI
gh repo create your-org/opentreasury-deploy --private --clone
cd opentreasury-deployOption B: Using the GitHub website
- Go to github.com/new
- Name it
opentreasury-deploy(or whatever you prefer) - Set it to Private
- Click Create repository
- Clone it to your computer:
git clone https://github.com/your-org/opentreasury-deploy.git cd opentreasury-deploy
Replace
your-orgwith your actual GitHub organization or username throughout this guide.
Download the OpenTreasury product code. You'll need it to run the setup script.
git clone https://github.com/rett-europe/opentreasury.git
cd opentreasuryOpen the setup script and edit the clearly-labeled variables at the top. There are only 5 values to change.
On Windows, open scripts\setup-azure.ps1 in any text editor (Notepad works fine).
On macOS/Linux, open scripts/setup-azure.sh in any text editor.
Edit these variables:
| Variable | What to put | Example |
|---|---|---|
PROJECT_NAME |
A short name for your deployment. Use lowercase letters only, no spaces. | opentreasury |
ENVIRONMENT |
prod for your live system, dev for testing |
prod |
LOCATION |
The Azure region closest to your organization. See Azure regions. | westeurope |
TENANT_DOMAIN |
Your organization's Entra ID domain. Find it in Azure Portal → Microsoft Entra ID → Overview → Primary domain. | myorg.onmicrosoft.com |
GITHUB_REPO |
Your deploy repo from Step 1, in owner/repo format. |
your-org/opentreasury-deploy |
Example (bash):
PROJECT_NAME="opentreasury"
ENVIRONMENT="prod"
LOCATION="westeurope"
TENANT_DOMAIN="myorg.onmicrosoft.com"
GITHUB_REPO="my-ngo/opentreasury-deploy"Example (PowerShell):
$ProjectName = "opentreasury"
$Environment = "prod"
$Location = "westeurope"
$TenantDomain = "myorg.onmicrosoft.com"
$GitHubRepo = "my-ngo/opentreasury-deploy"Leave the other variables alone. The "derived names" section below will calculate the rest automatically.
This step creates everything you need in Azure: a resource group, database, web hosting, identity configuration, and more. It takes about 10–15 minutes to run.
az loginThis opens your browser. Sign in with an account that has admin access to your organization's Entra ID.
On macOS/Linux (bash):
chmod +x scripts/setup-azure.sh
./scripts/setup-azure.shOn Windows (PowerShell):
.\scripts\setup-azure.ps1The script will:
- Ask you to confirm your Azure subscription
- Create a resource group with all infrastructure (database, web app, key vault, etc.)
- Create identity configurations for login (Entra ID app registrations)
- Create an OIDC federated credential for GitHub Actions (no permanent passwords stored)
- Print a table of values you'll need in the next step
The script is safe to re-run. If something goes wrong halfway through, or if you need to change a setting, just run it again. It detects what already exists and skips those steps — it won't create duplicates.
When the script finishes, it prints a table of GitHub configuration values. Keep this terminal open — you'll need these values in the next step.
The output looks like this:
▶ GitHub Configuration Summary
GitHub Secret (sensitive — Settings → Secrets):
╔════════════════════════════════════════╦═══════════════════════╗
║ AZURE_STATIC_WEB_APPS_API_TOKEN ║ <long token> ║
╚════════════════════════════════════════╩═══════════════════════╝
GitHub Variables (non-sensitive — Settings → Variables):
╔════════════════════════════════════════╦═══════════════════════╗
║ AZURE_CLIENT_ID ║ xxxxxxxx-xxxx-... ║
║ AZURE_TENANT_ID ║ xxxxxxxx-xxxx-... ║
║ AZURE_SUBSCRIPTION_ID ║ xxxxxxxx-xxxx-... ║
║ AZURE_RESOURCE_GROUP ║ rg-opentreasury-prod ║
║ AZURE_WEBAPP_NAME ║ app-opentreasury-prod ║
║ ENTRA_API_CLIENT_ID ║ xxxxxxxx-xxxx-... ║
║ MSAL_CLIENT_ID ║ xxxxxxxx-xxxx-... ║
║ MSAL_API_SCOPE ║ api://opentreasury-.. ║
║ SWA_HOSTNAME ║ blue-coast-abc123... ║
╚════════════════════════════════════════╩═══════════════════════╝
The deployment workflows need to know your Azure configuration. You'll set 1 Secret (sensitive) and 11 Variables (non-sensitive) in your deploy repo's GitHub settings.
- Go to your deploy repo on GitHub:
https://github.com/your-org/opentreasury-deploy - Click Settings (top menu bar)
- In the left sidebar, click Secrets and variables → Actions
On the Secrets tab, click New repository secret and add:
| Secret name | Where to find the value |
|---|---|
AZURE_STATIC_WEB_APPS_API_TOKEN |
The AZURE_STATIC_WEB_APPS_API_TOKEN value from the script output |
This is the only secret. It's a deployment token for the web frontend. All other values are non-sensitive and go in Variables.
Click the Variables tab, then click New repository variable for each:
| Variable name | Where to find the value | What it's for |
|---|---|---|
AZURE_CLIENT_ID |
The OIDC app registration client ID from script output | Identifies the deployment service principal |
AZURE_TENANT_ID |
The Tenant ID from script output | Your Entra ID tenant |
AZURE_SUBSCRIPTION_ID |
The Subscription ID from script output | Your Azure subscription |
AZURE_RESOURCE_GROUP |
The resource group name (e.g., rg-opentreasury-prod) |
Where your Azure resources live |
AZURE_WEBAPP_NAME |
The App Service name (e.g., app-opentreasury-prod) |
Your backend API host |
ENTRA_API_CLIENT_ID |
The API Client ID from script output | Backend API identity for login validation |
MSAL_CLIENT_ID |
The SPA Client ID from script output | Frontend login configuration |
MSAL_API_SCOPE |
api://opentreasury-api/access_as_user (uses your project name) |
API permission scope |
SWA_HOSTNAME |
The SWA hostname from script output (e.g., blue-coast-abc123.azurestaticapps.net) |
Your web app's address |
PROJECT_NAME |
The project name you chose (default: opentreasury) |
Used in Azure resource naming |
ENVIRONMENT_NAME |
prod (or dev if you provisioned a dev environment) |
Must match how you provisioned resources |
Double-check every value. A typo here will cause deployment failures that are hard to diagnose. Copy-paste from the script output whenever possible.
Copy the deployment workflow files from the product repo to your deploy repo.
On macOS/Linux:
# From the product repo root
cp -r deploy-template/.github ../opentreasury-deploy/On Windows (PowerShell):
# From the product repo root
Copy-Item -Recurse deploy-template\.github ..\opentreasury-deploy\Then push the files to GitHub:
cd ../opentreasury-deploy
git add .
git commit -m "Add deployment workflows"
git pushYour deploy repo should now contain:
opentreasury-deploy/
└── .github/
└── workflows/
├── deploy.yml # Deploys the application code
└── deploy-infra.yml # Deploys Azure infrastructure changes
The first deployment is a two-step process. After this, you'll only need deploy.yml for routine updates.
This ensures your Azure infrastructure matches the latest Bicep definitions.
- Go to your deploy repo on GitHub
- Click Actions (top menu)
- In the left sidebar, click Deploy Infrastructure
- Click Run workflow → leave
product_refasmain(or use a release tag likev1.0.0) → click the green Run workflow button - Wait for it to complete (2–5 minutes)
- Still in Actions, click Deploy in the left sidebar
- Click Run workflow
- Set
product_refto the version you want to deploy (e.g.,v1.0.0for the latest release, ormainfor the development version) - Click the green Run workflow button
- Wait for it to complete (5–10 minutes)
First deployment may take extra time. Azure needs 5–10 minutes to activate security permissions (RBAC propagation) on the first run. The workflow automatically retries the health check to handle this. Subsequent deployments are much faster.
Run through this checklist to confirm everything is working:
- Web app loads: Open
https://<your-SWA-hostname>in your browser. You should see the OpenTreasury login page. - Login works: Click "Sign in" and authenticate with your organization's Microsoft account. You should land on the dashboard.
- API responds: Open
https://<your-app-service-name>.azurewebsites.net/api/healthin your browser. You should see a JSON response with"status": "healthy". - Workflows succeeded: Check GitHub Actions — both
Deploy InfrastructureandDeployshould show green checkmarks.
Replace
<your-SWA-hostname>with yourSWA_HOSTNAMEvalue (e.g.,blue-coast-abc123.azurestaticapps.net) and<your-app-service-name>with yourAZURE_WEBAPP_NAMEvalue (e.g.,app-opentreasury-prod).
If any of these fail, see Troubleshooting below.
Congratulations — OpenTreasury is running! Here's how to start using it.
Before you can manage data, you need the Admin role. OpenTreasury uses Entra ID roles to control access.
- Go to portal.azure.com
- In the search bar at the top, type Enterprise Applications and select it
- Find your API app registration (named something like
opentreasury-api-prod)- If you don't see it, change the Application type dropdown to All Applications
- Click on it → Users and groups (left sidebar)
- Click Add user/group
- Under Users, click None Selected → search for your name → select yourself → click Select
- Under Role, click None Selected → choose Admin → click Select
- Click Assign
Important: Use Enterprise Applications, not App registrations. They look similar but serve different purposes. Enterprise Applications is where you assign roles to people.
- Open your OpenTreasury URL (
https://<your-SWA-hostname>) - Click Sign in with your Microsoft account
- If prompted, consent to the app permissions
- You should see an empty dashboard — ready to set up
- Navigate to Accounts in the sidebar
- Click New Account
- Enter the bank name (e.g., "CaixaBank — Main") and the IBAN
- Click Save
Repeat for each bank account your organization uses.
- Navigate to Categories in the sidebar
- Create categories that match how your organization classifies spending (e.g., "Therapies", "Office Supplies", "Travel")
- Add subcategories under each category for finer detail
- Navigate to Import in the sidebar
- Click Upload
- Select an Excel file exported from your bank
- Map the columns (date, description, amount) to OpenTreasury's fields
- Review the preview and click Import
You can give other people in your organization access to OpenTreasury. There are two roles:
| Role | What they can do |
|---|---|
| Admin | Everything — manage transactions, accounts, categories, tags, import data |
| Viewer | Read-only — view transactions, reports, and data but cannot change anything |
- Go to portal.azure.com
- Search for Enterprise Applications in the top search bar
- Find your API app (e.g.,
opentreasury-api-prod) - Click Users and groups → Add user/group
- Select the user, then select the role (Admin or Viewer)
- Click Assign
The user can now sign in at your OpenTreasury URL with their organization Microsoft account.
The OpenTreasury team releases updates with new features and bug fixes. Here's how to stay current.
- Go to github.com/rett-europe/opentreasury
- Click Watch (top right) → Custom → check Releases → click Apply
You'll get an email when a new version is published.
Versions follow a vMAJOR.MINOR.PATCH pattern (e.g., v1.2.3):
| Change type | Example | What to do |
|---|---|---|
| PATCH (v1.2.0 → v1.2.1) | Bug fixes, minor improvements | Just redeploy — safe and quick |
| MINOR (v1.2.x → v1.3.0) | New features, no breaking changes | Redeploy — new features available immediately |
| MAJOR (v1.x → v2.0.0) | Significant changes, possible data migration | Read the release notes carefully and follow the migration guide |
- Go to your deploy repo → Actions → Deploy
- Click Run workflow
- Set
product_refto the new version tag (e.g.,v1.3.0) - Click Run workflow
For MAJOR version upgrades, also check the release notes for:
- Whether you need to run
Deploy Infrastructurefirst (for infrastructure changes) - Whether there are data migration steps
If a new version causes problems, you can instantly go back to the previous working version.
- Go to your deploy repo → Actions → Deploy
- Click Run workflow
- Set
product_refto the previous version tag (e.g.,v1.2.0) - Click Run workflow
That's it. The previous version is deployed, and your data is unchanged.
Check: Did the Deploy workflow complete successfully?
- Go to your deploy repo → Actions
- Look at the most recent
Deployrun. Is it green (✅)? - If it failed, click on it and look at the failing step for error details.
Fix: If the workflow failed during the deployment step, try running it again — transient Azure errors sometimes resolve on retry.
Check: Is the SWA hostname registered as a redirect URI?
- Go to portal.azure.com
- Search for App registrations → find your SPA app (e.g.,
opentreasury-spa-prod) - Click Authentication → check that your SWA hostname appears under Single-page application redirect URIs
- It should show:
https://<your-SWA-hostname>
Fix: If the redirect URI is missing, click Add URI, paste https://<your-SWA-hostname>, and click Save. The setup script adds this automatically, but if the SWA wasn't ready when the script ran, it may have been skipped.
Check: Are the Key Vault secrets accessible?
- Go to Azure Portal → search for your App Service (e.g.,
app-opentreasury-prod) - Click Configuration → Application settings
- Look for settings that start with
@Microsoft.KeyVault(...)— they should show a green checkmark ✅ - If they show a red ❌, the App Service can't read from Key Vault
Fix: This usually means the App Service's managed identity doesn't have permission to read Key Vault secrets. Re-run the setup script — it will fix the permissions without duplicating resources.
Check: Does the service principal have sufficient permissions?
The infrastructure workflow needs Contributor and User Access Administrator roles on the resource group. The setup script creates these, but they can take a few minutes to activate.
Fix: Wait 5 minutes and try again. If it still fails, check the workflow logs for the specific error message.
This is normal. On the very first deployment, Azure needs 5–10 minutes to activate security permissions (called RBAC propagation). The health check retries automatically, but if all 3 attempts fail:
Fix: Wait 10 minutes, then manually run the Deploy workflow again. The permissions will be active by then.
This is expected behavior. The Bicep infrastructure template replaces all App Service settings with the ones defined in the template. Any settings you added manually through the Azure Portal will be removed.
Fix: All configuration must go through the Bicep templates. If you need custom settings, add them to your infrastructure definition rather than setting them in the Portal.
This is by design. OpenTreasury disables Cosmos DB key-based authentication for security. The database only accepts identity-based access (Managed Identity).
Fix: To browse your data in the Azure Portal, use the Entra ID-based authentication option in Data Explorer, not a connection string.
To keep an eye on what you're spending:
- Go to portal.azure.com
- Search for Cost Management in the top search bar
- Click Cost analysis (left sidebar)
- At the top, set the Scope to your resource group (e.g.,
rg-opentreasury-prod) - You'll see a breakdown of costs by resource
Tip: Set up a budget alert to get notified if costs exceed a threshold. In Cost Management → Budgets → Add, set a monthly budget (e.g., €30) and configure email alerts at 80% and 100%.
If you need to completely remove OpenTreasury from your Azure subscription — for example, if you're moving to a different setup or no longer need it — use the teardown script.
⚠️ This permanently deletes all your data and resources. This cannot be undone.
Before tearing down, export any data you want to keep using OpenTreasury's export feature (Transactions → Export).
On macOS/Linux:
cd opentreasury
chmod +x scripts/teardown-azure.sh
./scripts/teardown-azure.shOn Windows (PowerShell):
cd opentreasury
.\scripts\teardown-azure.ps1The script will ask you to type DELETE to confirm. It removes:
- The resource group (and everything in it: database, web app, key vault, etc.)
- The Entra ID app registrations
- The service principal
After teardown, you can also delete your GitHub deploy repo if you no longer need it.
| What | Where |
|---|---|
| Your web app | https://<SWA_HOSTNAME> |
| API health check | https://<AZURE_WEBAPP_NAME>.azurewebsites.net/api/health |
| Azure Portal | portal.azure.com |
| Deploy a new version | Deploy repo → Actions → Deploy → Run workflow |
| Add users | Azure Portal → Enterprise Applications → your app → Users and groups |
| Check costs | Azure Portal → Cost Management → Cost analysis |
| Product releases | github.com/rett-europe/opentreasury/releases |