_ ______ _ ____ _____
/ \ |__ / | | | _ \| ____|
/ _ \ / /| | | | |_) | _|
/ ___ \ / /_| |_| | _ <| |___
/_/ \_\/____|\___|_| \_\_____|
_ _ _ _ _ __ ____________ ____
/ \ | \ | | / \ | | \ \ / /__ / ____| _ \
/ _ \ | \| | / _ \ | | \ V / / /| _| | |_) |
/ ___ \| |\ |/ ___ \| |___| | / /_| |___| _ <
/_/ \_\_| \_/_/ \_\_____|_| /____|_____|_| \_\
One PowerShell command, 38 read-only assessment tools (+ 1 opt-in), one unified HTML and Markdown report. Cloud-first by default: target remote GitHub and Azure DevOps repositories without cloning anything by hand.
- samples/sample-report.html (v2 mockup), rendered HTML report with the framework coverage matrix, severity heatmap, top risks, and interactive findings exploration.
- samples/sample-report.md (v2 mockup), Markdown report variant with the same posture summary, framework coverage, and detailed findings for Git-native review flows.
- Unified findings JSON with Schema 2.2 metadata (severity, frameworks, MITRE mappings, remediation snippets, evidence URIs, entity references, deep links).
- Interactive HTML report with sortable findings tree, framework heatmap, CSV export.
- Markdown report for Git commit workflows.
- Entity store (JSON) with deduplicated cross-tool findings.
- Resilience map renderer (Track B) with tier-aware heatmap cells, edge-style resolution, blast-radius traversal, and graceful RTO/RPO overlay fallback.
- External Attack Surface Management (EASM, in flight): passive perimeter discovery from typosquats (DNSTwist) and, in follow-up PRs, subdomain enumeration (Amass / Subfinder), host probing (httpx), commercial scans (Shodan / Censys), and Microsoft Defender EASM workspace consumption. Findings correlate back to existing
AzureResourceentities or fall back to a newExternalAssetbucket. Seedocs/design/easm-integration.md. - Graph mapping (in flight): Conditional Access policy graph (R1, enabled) emits
ConditionalAccessPolicyentities plusAppliesTo/Excludesedges to users, groups, applications, and named locations, alongside a five-rule risk rubric for disabled-covers-privileged, GA-excluded-from-MFA, report-only stagnation, oversized break-glass, and no-strong-control gaps. Pre-registered family for follow-up PRs: AzureHound, BloodHound CE, ROADrecon, GraphRunner, PIM eligibility / activation graph, Microsoft Entra Permissions Management, and Semperis Forest Druid. Seedocs/design/graph-mapping-integration.md. - Optional local findings viewer scaffold (
-Show) that starts on127.0.0.1with a session token and health probe.
The module manifest, tags, and release pipeline are PSGallery-ready. The first
release tag (v*.*.*) on main triggers the release.yml PSGallery publish
step, which uploads AzureAnalyzer.psd1 + AzureAnalyzer.psm1 (plus the
manifest-listed runtime files) under the AzureAnalyzer package id. Once the
first publish lands, this is the recommended install path:
Install-Module -Name AzureAnalyzer -Scope CurrentUser
Import-Module AzureAnalyzer
Connect-AzAccount -TenantId "<tenant-id>"
Invoke-AzureAnalyzer -SubscriptionId "<subscription-id>"PSGallery ships only the orchestrator and report renderers. Every external scanner (azqr, PSRule, Maester, Trivy, ...) is a soft dependency and is fetched on demand by the manifest-driven installer at first run. See the External tools (soft dependencies) section below for the full list of what is installed where.
git clone https://github.com/martinopedal/azure-analyzer.git && cd azure-analyzer
Import-Module .\AzureAnalyzer.psd1
Connect-AzAccount -TenantId "<tenant-id>"
Invoke-AzureAnalyzer -SubscriptionId "<subscription-id>" # outputs to output/, report.html, report.md# Run against bundled test fixtures, no Azure login required
.\Invoke-AzureAnalyzer.ps1 -FixtureMode -OutputPath .\output-fixtureProduces real results.json, entities.json, HTML and Markdown reports from fixture data in tests/fixtures/. Useful for contributors, CI pipelines, and demo environments without cloud credentials. Use -FixturePath <dir> to supply custom fixtures.
Generate control-centric compliance reports with evidence-grade citations and framework coverage:
# Run with Auditor profile
Invoke-AzureAnalyzer -SubscriptionId "<subscription-id>" -Profile Auditor
# Outputs:
# output/audit-report.html - Auditor-focused HTML with control-domain sections
# output/audit-report.md - Markdown audit report variant
# output/audit-evidence/*.csv - Evidence exports (CSV, JSON, XLSX)
# output/audit-evidence/*.json
# output/audit-evidence/*.xlsxAuditor mode produces:
- Executive summary with scope, methodology, and severity distribution
- Control-domain sections grouped by framework (CIS, NIST, MCSB, ISO 27001)
- Attack-path and blast-radius analysis from Track A/B data
- Policy coverage vs. ALZ reference gaps
- Remediation appendix grouped by finding type
- Evidence exports sanitized of all credentials
See docs/design/track-f-auditor-redesign.md for the full design.
See docs/getting-started for installation, first run, and common scenarios →
Feature highlights
- 38 tools (+ 1 opt-in) across Azure (azqr, PSRule, Powerpipe, AzGovViz, Prowler, Defender for Cloud, ...), Entra (Maester, Identity Correlator, ...), GitHub (gitleaks, Trivy, Scorecard, zizmor), and Azure DevOps (pipeline security, service connections, repos).
- Schema 2.2 metadata: Frameworks, Pillar, Impact, Effort, DeepLinkUrl, RemediationSnippets, EvidenceUris, BaselineTags, MitreTactics, MitreTechniques, EntityRefs, ToolVersion.
- Phase 0 foundation contract: report architecture is selected with explicit thresholds and headroom,
output/report-manifest.jsoncaptures picker and verification metadata, and normalizers can contribute edges through optional EdgeCollector introspection without breaking legacy signatures. - Attack-path renderer (Track A):
AttackPathRenderernow emits a tier-aware Cytoscape model/data-island with a shared edge budget report, finding pivot map, and hydration hints for Tier 1-4. - Policy enforcement track (Track C): ALZ hierarchy fuzzy-match scoring (Round 2 weights), vendored AzAdvertizer + ALZ catalog lookups, AzGovViz policy-edge emission (
PolicyAssignedTo,PolicyEnforces,ExemptedFrom,InheritsFrom), and policy metadata inreport-manifest.json(policy.alz.mode). Use-AlzReferenceMode {Auto|Force|Off}to control ALZ suggestion activation. - Read-only everywhere. No write permissions on any cloud. See PERMISSIONS.md for exact scopes.
- HTML and Markdown reports with executive summary, top recommendations, heatmap, framework coverage matrix, filtering, and CSV export.
- Manifest-driven installer: Run with
-InstallMissingModulesto auto-fetch prerequisites (PSGallery modules, allow-listed package managers, HTTPS-only git clones). - Hardened Log Analytics sink errors: sink failures now use structured
New-FindingErrormessages with mandatory remediation and credential-safe details, and sink raw-throw regressions are ratcheted in tests. - Uniform correlator dispatch:
identity-correlatornow uses a thinInvoke-*wrapper entrypoint while keeping correlation logic in shared modules. - Shared credential sanitization contract: CI watchdog issue-body generation now reuses
modules/shared/Sanitize.ps1::Remove-Credentials(no local sanitizer drift). - Pre-flight required-input resolution: required tool inputs are collected before dispatch using
CLI > environment > prompt > fail-fastwith non-interactive safety. - Consistent wrapper error exits: Falco, KubeBench, Kubescape, DefenderForCloud, Gitleaks, AksKarpenterCost, and AksRightsizing now emit structured
New-FindingError/Format-FindingErrorMessagethrows instead of raw throw strings. - Repo input consistency: repo-scoped wrappers now use
-RepoPath(local) and-RemoteUrl(remote) as canonical inputs, with legacy aliases preserved for compatibility. - Mandatory scanner-param prompts (#426): when a scanner is selected but its mandatory parameter is missing,
Read-MandatoryScannerParamresolves it via env var (AZURE_SUBSCRIPTION_ID,AZURE_TENANT_ID,ADO_ORG,GITHUB_REPOSITORY,AZUREANALYZER_REPO_PATH) or interactive prompt. CI /-NonInteractiveruns skip the scanner with a sanitized warning instead of failing late inside the underlying tool. - Safer dry-runs for side-effecting wrappers:
Invoke-Falcoand elevatedInvoke-AksKarpenterCostnow support-WhatIf/-Confirm(SupportsShouldProcess) before cluster install/elevated kubectl actions. - ADO consumption parameter consistency:
Invoke-AdoConsumptionnow uses canonical-AdoOrg/-AdoProjectnames, with legacy-Organization/-Projectaliases retained for backward compatibility. - Opt-in LLM triage scaffold: defaults to 3-model rubberduck consensus, discovers your available Copilot models at runtime (
gh copilot models list), supports-TriageModel Auto|Explicit:<model-id>plus explicit-SingleModelopt-out, and applies credential sanitization on prompt + response paths.
Tool catalog (38 enabled + 1 opt-in)
See docs/reference/tool-catalog.md. The catalog documents each tool's scope axis (subscription, managementGroup, tenant, repository, ado, or workspace), which maps directly to the -SubscriptionId / -ManagementGroupId / -TenantId / -RepoPath (or -RemoteUrl) / -AdoOrg / -WorkspaceResourceId parameters on Invoke-AzureAnalyzer.ps1.
Tool output audit (Track D)
See docs/tool-output-audit.md and docs/tool-output-audit.json for per-tool wrapper-vs-normalizer field-coverage entries (audit-first input for #432b FindingRow extension).
The PSGallery package ships only the orchestrator wrappers, normalizers, report
renderers, and tools/tool-manifest.json. It does not bundle the upstream
scanners. At first run, Install-PrerequisitesFromManifest (in
modules/shared/Installer.ps1) reads the manifest and installs each enabled
tool through one of four kinds:
install.kind |
What it does | Where it lands |
|---|---|---|
psmodule |
Install-Module from PSGallery (with retry + 300s timeout) |
User scope module path |
cli |
Allow-listed package manager (winget / brew / pipx / pip / snap) |
Manager-specific install location |
gitclone |
HTTPS-only git clone against the host allow-list (github.com, dev.azure.com, *.visualstudio.com, *.ghe.com) |
tools/_vendored/<tool>/ |
none |
Pure PowerShell or REST against an existing SDK / CLI; nothing to install | n/a |
Every install path is wrapped in Invoke-WithInstallRetry + Invoke-WithTimeout
so a stuck winget / brew session cannot hang the orchestrator. Failures throw
structured New-InstallerError records (with Category, Remediation, and a
sanitized Details payload) and the affected tool is skipped, not the whole
run.
To install everything up front instead of lazily on first use:
Invoke-AzureAnalyzer -InstallMissingModules ...The 38 enabled tools (+ 1 opt-in) break down as follows. The full per-tool
catalog with parameters, frameworks, and required scopes lives in
docs/reference/tool-catalog.md; permissions
lives in PERMISSIONS.md and
docs/consumer/permissions/.
psmodule install kind unless noted.
| Tool | Scope | Install | What it scans |
|---|---|---|---|
| azqr | subscription | cli | Azure Quick Review posture across resource types |
| psrule | subscription | psmodule | PSRule for Azure rule pack |
| powerpipe | subscription | cli | Powerpipe compliance benchmarks (CIS, NIST, PCI) |
| prowler | subscription | cli | Prowler Azure security posture |
| defender-for-cloud | subscription | psmodule | Defender for Cloud recommendations + secure score |
| finops | subscription | psmodule | Idle resource detection (FinOps signals) |
| azure-cost | subscription | psmodule | Azure Consumption API top-N cost hotspots |
| azure-quota | subscription | cli | Quota and usage reports |
| appinsights | subscription | psmodule | Application Insights performance signals |
| loadtesting | subscription | psmodule | Azure Load Testing failed and regressed runs |
| falco | subscription | psmodule | Falco AKS runtime anomaly detection (helm install opt-in) |
| kubescape | subscription | cli | Kubescape AKS posture |
| kube-bench | subscription | none | kube-bench AKS node CIS compliance (kubectl Job) |
| aks-rightsizing | subscription | psmodule | Container Insights utilization + size recommendations |
| aks-karpenter-cost | subscription | psmodule | Karpenter consolidation + node utilization (opt-in elevated RBAC) |
| sentinel-coverage | workspace | psmodule | Microsoft Sentinel coverage and posture |
| sentinel-incidents | workspace | psmodule | Microsoft Sentinel active incidents |
| azgovviz | managementGroup | gitclone | AzGovViz tenant governance + policy report |
| alz-queries | managementGroup | psmodule | ALZ Resource Graph compliance queries (50+) |
| wara | subscription | psmodule | Well-Architected Reliability Assessment |
| Tool | Provider | Install | What it scans |
|---|---|---|---|
| maester | microsoft365 | psmodule | Maester Entra and M365 security baseline |
| identity-correlator | graph | psmodule | Cross-source identity correlation (Entra + Azure RBAC) |
| identity-graph-expansion | graph | psmodule | Identity graph expansion (group, role, app assignments) |
| conditional-access-graph | graph | psmodule | Conditional Access policy graph + 5-rule risk rubric |
Disabled-by-default Graph family (scaffolded for follow-up PRs):
azurehound, bloodhound-ce, roadrecon, graphrunner, pim-graph,
entra-permissions-mgmt, forest-druid. See
docs/design/graph-mapping-integration.md.
| Tool | Provider | Install | What it scans |
|---|---|---|---|
| scorecard | github | cli | OpenSSF Scorecard threshold check |
| gh-actions-billing | github | cli | GitHub Actions minutes + monthly budget governance |
| gitleaks | cli | cli | gitleaks secrets scanner (cloud-first via RemoteClone.ps1) |
| trivy | cli | cli | Trivy vulnerability scanner (filesystem or repo) |
| zizmor | cli | cli | zizmor GitHub Actions YAML scanner |
| bicep-iac | cli | cli | Bicep IaC validation |
| terraform-iac | cli | cli | Terraform IaC validation |
| infracost | cli | cli | Infracost IaC cost estimation |
install.kind = none for every ADO tool: pure REST through an existing PAT.
| Tool | Scope | What it scans |
|---|---|---|
| ado-pipelines | ado | Pipeline security posture (auth modes, approvals, secrets) |
| ado-connections | ado | Service connection inventory + auth posture |
| ado-repos-secrets | ado | Repos secret scanning (gitleaks-driven) |
| ado-pipeline-correlator | ado | Cross-pipeline run correlator (ties findings to runs) |
| ado-consumption | ado | Pipeline consumption + monthly budget governance |
dnstwist (cli) ships enabled. The remaining EASM family
(amass, subfinder, httpx, shodan, censys, defender-easm) is
scaffolded as enabled: false for follow-up PRs. See
docs/design/easm-integration.md.
| Tool | Trigger | Notes |
|---|---|---|
| copilot-triage | -EnableAiTriage |
Routes findings through GitHub Copilot for AI triage; defaults to a 3-model rubberduck consensus, discovers your available models at runtime, applies credential sanitization on prompt + response paths. |
The single source of truth is tools/tool-manifest.json. If you want to add or
toggle a tool, edit the manifest and the catalog, README counts, and
PERMISSIONS index regenerate automatically through tools/Update-ToolPins.ps1
(weekly bumper) or by running the three generators by hand. See
docs/reference/tool-catalog-contributor.md
for the contributor view.
All parameters and advanced usage
See docs/reference/orchestrator-params.md for every Invoke-AzureAnalyzer.ps1 parameter, plus scenario-specific grouping (subscription scan, repo scan, multi-tenant, CI/CD, Kubernetes).
After Import-Module .\AzureAnalyzer.psd1, Invoke-AzureAnalyzer now exposes the same typed parameters in Get-Command / Get-Help as the script entrypoint.
Environment variables
azure-analyzer honours a small set of opt-in environment variables for CI / quiet-mode use:
AZUREANALYZER_NO_BANNER=1-- suppress the ASCII banner. Also auto-suppressed whenCI=trueorGITHUB_ACTIONS=true.AZURE_ANALYZER_SUPPRESS_TOOL_MISSING_WARNINGS=1-- silence<tool> is not installed. Skipping...notices from every wrapper. Routes throughWrite-Verboseinstead. Belt-and-suspenders kill-switch for noisy CI / Pester transcripts (#472). Truthy values:1,true,yes,on(case-insensitive).AZURE_ANALYZER_ORCHESTRATED=1(set automatically byInvoke-AzureAnalyzer.ps1) -- tells wrappers they were launched by the orchestrator, not standalone.AZURE_ANALYZER_EXPLICIT_TOOLS=trivy,gitleaks,...(set automatically) -- comma-separated CSV of tools the user named via-IncludeTools. Empty when no filter was passed.
See docs/contributing/ to add a new tool, extend the orchestrator, or contribute documentation. The architecture docs cover how azure-analyzer works under the hood, and design proposals belong under docs/design/.
Invoke-Pester -Path .\tests -CI: full Pester suite (baseline is enforced bytests/workflows/PesterBaselineGuard.Tests.ps1and grows over time).Invoke-Pester -Path .\tests\wrappers -CI: wrapper contract suite, including E2E wrapper-to-normalizer coverage for zizmor, gitleaks, and trivy.Invoke-Pester -Path .\tests\wrappers -Tag 'LiveTool' -CI: optional live-CLI wrapper smoke tier (gitleaks, trivy, zizmor, scorecard) that exercises real binaries when present and enforces deterministic v1 envelopes (Findingsis always an array).Invoke-Pester -Path .\tests\e2e -Output Detailed: end-to-end harness that drivesInvoke-AzureAnalyzer's output pipeline (FindingRow → EntityStore →results.json+entities.json→ HTML + Markdown reports) across three surfaces (Azure subscription, GitHub repo, Tenant / management-group) with synthetic fixtures undertests/e2e/fixtures/.Invoke-Pester -Path .\tests\wrappers\MissingToolRuntime.Tests.ps1: cross-platform runtime coverage for missing-tool behavior inInvoke-Trivy,Invoke-Kubescape, andInvoke-Scorecard.Invoke-Pester -Path .\tests\e2e\WrapperCoverageParity.Tests.ps1 -CI: validates E2E wrapper coverage tracker stays in lockstep withtools/tool-manifest.json.Invoke-Pester -Path .\tests\shared\TestIsolation.Tests.ps1 -CI: guard rail for cross-file state leaks.
- Required status check:
Analyze (actions), the only check enforced in branch protection. PowerShell is not scanned by CodeQL (no supported extractor); Actions scanning covers workflow injection risks. Auto-RebaseandRerun-Failed-Checksskip on non-agent branches: ThePR Auto-Rebase ConflictsandPR Auto-Rerun On Pushworkflows only trigger on agent-owned branch prefixes (squad/,copilot/,fix/,feat/,ci/,docs/). If you push from a branch without one of these prefixes, the workflows will show as skipped. This is expected behavior, not a failure.- Manifest hygiene: keep
tools/tool-manifest.jsonentries alphabetized by toolname(case-insensitive); this is enforced bytests/manifest/Manifest.Sorted.Tests.ps1. - CodeQL (
Analyze (actions)) uses a global workflow concurrency queue to reduce GitHub App installation API throttling during PR bursts. - Workflow hotfix-debt contract: every
.github/workflows/*.ymlcontinue-on-error: truedirective must carry an inline tracking marker comment (# tracked: martinopedal/azure-analyzer#604 - hotfix-debt) immediately above it. - Markdown Check hardening: the
links (lychee)job scopes PR runs to changed Markdown files (scheduled/manual runs still scan the full corpus), clears.lycheecachebetween retry attempts, and passesGITHUB_TOKENto reduce transient GitHub/rate-limit flakes. - Scheduled Scan requires OIDC repo variables (
AZURE_SUBSCRIPTION_ID,AZURE_TENANT_ID,AZURE_CLIENT_ID). Scheduled runs skip analyzer execution without going red when these variables are not configured; manual dispatch still fails fast on missing or malformed values.
Maintainer notes
See CONTRIBUTING.md for full CI workflow and squad infrastructure (maintainer-only) documentation. The docs/contributor/ directory covers contributor setup and development workflows. The .squad/ and .copilot/skills/ subtrees contain AI team coordination state and are excluded from archive downloads. They are not part of the shipped tool.
Maintainer notes
See CONTRIBUTING.md for full CI workflow and squad infrastructure (maintainer-only) documentation. The docs/contributor/ directory covers contributor setup and development workflows. The .squad/ and .copilot/skills/ subtrees contain AI team coordination state and are excluded from archive downloads. They are not part of the shipped tool.