What problem does this solve?
The 27 network rules (AZ-NET-001 to AZ-NET-027) are the largest rule family. These rules only implement scan(), so the engine reports their coverage as UNKNOWN / LEGACY_RULE_NOT_MIGRATED, and every compliance control mapped to them reads UNKNOWN in the evaluation-derived reports introduced in #310.
Describe the solution
Migrate the rules below to the evaluate() contract defined in scanner/evaluation.py, following the storage reference implementation (STORAGE_REF).
Rules in scope: az_net_001 … az_net_027, plus the shared helpers _perimeter_common.py, _private_link_common.py and _data_link_common.py where they are used by network rules.
Delivery: two PRs to keep review manageable. PR A covers NSG / public exposure / perimeter rules. PR B covers private link, firewall, WAF and routing rules. Where a network list helper returns [] on failure, add a list_*() variant returning Optional[List] as in the foundation issue.
Status mapping
Every path that scan() currently handles with a silent continue must become an explicit status:
| Situation |
Status |
reason_code |
Inventory call failed (returned None) |
ERROR |
INVENTORY_UNAVAILABLE |
| Inventory succeeded but is empty |
NOT_APPLICABLE |
NO_RESOURCES_FOUND |
| Setting is compliant |
PASS |
— |
| Setting is a violation |
FAIL + the existing finding dict |
— |
A per-resource detail call returned None, or a required field is missing |
UNKNOWN |
EVIDENCE_UNAVAILABLE / MISSING_PROPERTIES |
Opt-in policy tag not set (policy_required(...) is false) |
NOT_APPLICABLE |
POLICY_NOT_REQUIRED |
| Approved exception tag set |
NOT_APPLICABLE |
APPROVED_EXCEPTION |
A failed inventory call must never produce PASS or NOT_APPLICABLE.
Acceptance criteria
Alternatives considered
Migrating one rule at a time was considered. Grouping by family lets each PR add the family's inventory helper once and review the shared common module once.
Additional context
What problem does this solve?
The 27 network rules (
AZ-NET-001toAZ-NET-027) are the largest rule family. These rules only implementscan(), so the engine reports their coverage asUNKNOWN / LEGACY_RULE_NOT_MIGRATED, and every compliance control mapped to them readsUNKNOWNin the evaluation-derived reports introduced in #310.Describe the solution
Migrate the rules below to the
evaluate()contract defined inscanner/evaluation.py, following the storage reference implementation (STORAGE_REF).Rules in scope:
az_net_001…az_net_027, plus the shared helpers_perimeter_common.py,_private_link_common.pyand_data_link_common.pywhere they are used by network rules.Delivery: two PRs to keep review manageable. PR A covers NSG / public exposure / perimeter rules. PR B covers private link, firewall, WAF and routing rules. Where a network list helper returns
[]on failure, add alist_*()variant returningOptional[List]as in the foundation issue.Status mapping
Every path that
scan()currently handles with a silentcontinuemust become an explicit status:reason_codeNone)ERRORINVENTORY_UNAVAILABLENOT_APPLICABLENO_RESOURCES_FOUNDPASSFAIL+ the existing finding dictNone, or a required field is missingUNKNOWNEVIDENCE_UNAVAILABLE/MISSING_PROPERTIESpolicy_required(...)is false)NOT_APPLICABLEPOLICY_NOT_REQUIREDNOT_APPLICABLEAPPROVED_EXCEPTIONA failed inventory call must never produce
PASSorNOT_APPLICABLE.Acceptance criteria
evaluate(azure_client, subscription_id) -> List[RuleEvaluation]using the shared helpers inscanner/evaluation.py.scan()becomes a thin wrapper returning the findings attached toFAILevaluations.scan()returns exactly the same findings as before the change.FAIL/PASSevaluation records the inspected value(s) inevidence, for example{"allow_blob_public_access": true}.tests/test_rule_evaluation_contract.pyand it passes.pytest,ruff check .andruff format --check .pass.CHANGELOG.mdupdated underUnreleased.Alternatives considered
Migrating one rule at a time was considered. Grouping by family lets each PR add the family's inventory helper once and review the shared common module once.
Additional context
tests/test_rules_network.py,tests/test_enterprise_perimeter_rules.py,tests/test_private_link_rules.py,tests/test_network_layer_assurance.py