How to get Bastion running from a clean machine. The offline path (steps 1–4) needs
no Azure account and no credentials — it analyses the captured estate snapshot in
samples/ and produces a real report. The live path (step 5) scans your
own subscriptions.
For what Bastion is and what the rules check, see README.md.
| Tool | Version | Needed for |
|---|---|---|
| .NET SDK | 8.0 | the scanner and the rule engine |
| Node.js | 20 LTS | web/ — the findings dashboard |
| Git | any recent | clone |
| Azure CLI | latest | only for live scans and az bicep build |
dotnet --version && node --version
git clone https://github.com/NickZivkovic1993/azure-access-baseline.git
cd azure-access-baseline
dotnet restore Bastion.sln
dotnet build Bastion.sln
cd web && npm ci && cd ..
Exactly what CI runs on every push:
dotnet test Bastion.sln # 15 rule-engine tests
cd web && npm run build && cd ..
Bicep validation is optional locally (it needs the Azure CLI):
az bicep build --file infra/main.bicep
dotnet run --project src/Bastion.Scanner -- rules
dotnet run --project src/Bastion.Scanner -- analyze samples/estate.json --format markdown
analyze reads the deliberately messy two-subscription snapshot in
samples/estate.json, runs the baseline, and prints the one-page
access review — 13 findings.
It exits 2, and that is correct. The scanner exits non-zero when High or Critical findings are open, so a scheduled pipeline fails on drift. Don’t read the exit code as a crash.
To see the findings in the dashboard instead:
dotnet run --project src/Bastion.Scanner -- analyze samples/estate.json --format json --output findings.json
cd web && npm run dev # :5173 — load findings.json
Scanning reads your Azure estate; it never writes to it.
Permissions the scanning identity needs:
Application.Read.All on Microsoft Graph — optional, but without it
GraphServicePrincipalReader
can’t read service-principal credentials, so BAS-002 finds nothing.Then:
az login
dotnet run --project src/Bastion.Scanner -- scan # JSON
dotnet run --project src/Bastion.Scanner -- scan --format sarif --output findings.sarif
dotnet run --project src/Bastion.Scanner -- scan --save-snapshot estate.json # capture for offline analysis
Authentication is DefaultAzureCredential — az login locally, a managed identity or
workload-identity federation in a pipeline. There are no keys or connection strings to
configure anywhere.
infra/ is the remediation as Bicep — the managed identity that replaces
shared secrets, OIDC federation for GitHub Actions, an RBAC-mode Key Vault, and
group-scoped privileged roles:
az deployment group create -g <resource-group> -f infra/main.bicep
Review the parameters first; this one does write to your subscription.
scan finds nothing / reports zero subscriptions — the identity has no Reader
role on anything, or az login selected the wrong tenant (az account show).BAS-002 never fires on a live scan — missing Application.Read.All on Graph.analyze/scan — by design: Critical or High findings are open.
Exit 0 means clean, 1 means the scan itself failed.az bicep build not found — run az bicep install once.