feat(azure): filtering scans at resource group level#10657
feat(azure): filtering scans at resource group level#10657Legin-ML wants to merge 23 commits intoprowler-cloud:masterfrom
Conversation
Signed-off-by: Legin-ML <leginml2004@gmail.com>
Signed-off-by: Legin-ML <leginml2004@gmail.com>
Signed-off-by: Legin-ML <leginml2004@gmail.com>
Signed-off-by: Legin-ML <leginml2004@gmail.com>
Signed-off-by: Legin-ML <leginml2004@gmail.com>
Signed-off-by: Legin-ML <leginml2004@gmail.com>
|
✅ Conflict Markers Resolved All conflict markers have been successfully resolved in this pull request. |
HugoPBrito
left a comment
There was a problem hiding this comment.
Thanks for the implementation. The overall direction matches the issue discussion, but I found three cases where --azure-resource-group can still return incorrect results, plus one validation/docs mismatch.
Main issues:
networkcan produce falseFAILresults becauseNetwork Watcherresources are filtered by the watcher's own resource group while the downstream checks still evaluate subscription-wide coverage.monitorcan produce falseFAILresults for activity log alert checks for the same reason.- Resource group validation is currently case-sensitive even though Azure resource group names are case-insensitive.
Suggested remediation plan:
- Classify Azure checks as RG-scoped, subscription/tenant-scoped, or cross-scope.
- For subscription-scoped or cross-scope checks that become ambiguous under RG filtering, skip them with a warning instead of returning a
FAIL. - Make RG validation case-insensitive and update the docs accordingly.
- Add targeted tests for
NetworkWatcherRG, activity log alerts living in a different resource group, and valid RG names provided with different casing.
|
Thanks for the review @HugoPBrito. I'll make the changes requested. Regarding test files, could you let me know if any changes are needed? If not I'll write test files for all of them. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #10657 +/- ##
==========================================
- Coverage 86.17% 81.77% -4.41%
==========================================
Files 223 223
Lines 5744 6376 +632
==========================================
+ Hits 4950 5214 +264
- Misses 794 1162 +368
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
|
Thanks @Legin-ML! Regarding tests: Yes, please add coverage for all 22 services — the current 1/22 is what's making Codecov fail (
On top of that, please add the targeted cases for the ambiguous subscription-scoped checks:
No need to rewrite the existing tests — just extend them with the parametrized RG-filter cases. If a helper for "build a mocked Azure provider with Ping me once it's ready and out of Draft and I'll re-review. If not, I'll be happy to take care of it myself. |
Signed-off-by: Legin-ML <leginml2004@gmail.com>
2e27af9 to
b86c49f
Compare
Signed-off-by: Legin-ML <leginml2004@gmail.com>
ee4230a to
73363f9
Compare
|
I have added a new class for 3 kinds of tests. with one RG, 2 RGs and mixed case RG. I have also set a |
HugoPBrito
left a comment
There was a problem hiding this comment.
Thanks for the PR — the feature is valuable and the test coverage is solid. Before approving, I'd like the following points addressed. I'm flagging as blocking anything that affects correctness or security (a user could silently lose findings) and as important anything that affects maintainability/UX.
Blocking
validate_resource_groupscan take down provider initialization if a single subscription fails (permissions/throttling). [comment onazure_provider.py:1091]- Type annotations declare
listfor what is actuallydict[str, list]— misleads readers and breaks type checkers. [comments onazure_provider.py:107and:348] print_credentialsshows[](instead ofALLor a clearer message) when no RG matches — the user sees an apparently "empty" scan with no signal. [comment onazure_provider.py:467]- Entra (all methods), Defender (
pricings/auto_provisioning/assessments/settings/security_contacts) and IAM (roles/role_assignments) silently skip without producing any findings when--azure-resource-groupis used. Monitor and Network checks do emit aMANUALfinding withstatus_extended. We need to homogenize so the user never loses visibility. [comments onentra_service.py:71,defender_service.py:39,iam_service.py:29]
Important
5. Heavy duplication of the if self.resource_groups: ... block across ~20 services. Should be extracted into a helper in lib/service/service.py. [comment on lib/service/service.py:23]
6. Inconsistent behavior when rgs is empty for a subscription (some continue, some don't), and the "No valid resource groups for subscription X" warning duplicates the one already emitted in validate_resource_groups. [comment on aisearch_service.py:25]
7. monitor_service.get_alert_rules still calls list_by_subscription_id() even though all monitor checks return MANUAL — wastes an API call and requires permissions the user may not have. [comment on monitor_service.py:79]
Happy to approve once these are sorted. I'm glad to help with whatever you need — pair on the shared helper, sketch the MANUAL-finding pattern for the tenant/subscription-scoped checks, review intermediate pushes, or take any of these items off your plate. Just ping me.
| _region_config: AzureRegionConfig | ||
| _locations: dict | ||
| _mutelist: AzureMutelist | ||
| _resource_groups: list |
There was a problem hiding this comment.
Blocking — type annotation mismatch.
The annotation says list, but validate_resource_groups returns dict[str, list[str]] (a subscription_name -> [rg_names] map) and every consumer in the *_service.py files treats it as a dict (self.resource_groups.get(sub, [])). Change the annotation to dict[str, list[str]] (or dict) to avoid misleading future maintainers and to keep type checkers happy.
Signed-off-by: Legin-ML <leginml2004@gmail.com>
|
Really thankful for the in-depth review @HugoPBrito. I have made the changes you requested. Please, take your time in reviewing it. I really appreciate the support here 😊 |
HugoPBrito
left a comment
There was a problem hiding this comment.
Thanks for working through everything in the previous round, @Legin-ML. The shared helper, the case-insensitive RG resolution with the real Azure name preserved, the MANUAL findings in Entra / Defender / IAM, the per-subscription try/except, the input trim, the print_credentials fallback, and the type annotations all look good. Reviewed at e2edd481.
I found two new issues introduced in feat: add manual to checks that need addressing before approval, plus a couple of minor follow-ups.
Blocking
-
All 11 monitor
activity_log_alertschecks now produce zero findings when--azure-resource-groupis active.
monitor_service.get_alert_rulesearly-returns{}(monitor_service.py:76-77), and every monitor alert check iteratesmonitor_client.alert_rules.items()— e.g.monitor_alert_create_update_nsg.py:13. With an empty dict, theforloop never executes, so the MANUAL branch added inside it is unreachable. This re-introduces the silent-skip problem the previous round fixed in Entra / Defender / IAM.
Two ways to fix it:- Keep the API-call skip but pre-populate the dict, e.g.
alert_rules = {sub: [] for sub in self.clients}before returning early; or - Have the checks iterate
monitor_client.subscriptionsinstead ofmonitor_client.alert_rules, mirroring the Defender / IAM pattern.
I'd lean toward the second option since it's the same shape used elsewhere and keeps the service free of "fake" entries.
- Keep the API-call skip but pre-populate the dict, e.g.
-
tests/providers/azure/azure_provider_test.py::test_validate_resource_groups_mixed_caseis failing.AssertionError: assert ['MyGroup'] == ['mygroup']The implementation now correctly returns Azure's real casing (
existing_rgs.get(rg.lower())), which is what we agreed on, but the test still asserts the user-typed casing. Update the assertion to expect["MyGroup"]. While you're there, also assert theexisting_rgs.list()mock was called only once per subscription (regression guard).
Important
-
validate_resource_groupsis still annotated-> dict:atazure_provider.py:1091. The property is nowdict[str, list[str]]; the function should match for consistency and to lock in the contract. -
The new docs page (
docs/user-guide/providers/azure/resource-groups.mdx) no longer claims case-sensitivity (good), but it also doesn't tell users it's case-insensitive. One sentence under "How It Works" would close the loop, e.g. "Resource group names are matched case-insensitively, mirroring Azure's own behavior."
Once the monitor checks emit MANUAL findings under RG filtering and the test is green I'm happy to approve. Ping me when ready.
Signed-off-by: Legin-ML <leginml2004@gmail.com>
Signed-off-by: Legin-ML <leginml2004@gmail.com>
|
Hi @HugoPBrito,
|
|
Hi @Legin-ML! Thank you very much for your hard work. At first glance, I do not see any blocking points anymore. Nevertheless, I still need to do some testing before approving. In the meantime, please make sure all CI/CD checks pass. Please ignore the SDK Duplicate one for the moment. |
|
A big PR was just merged. @Legin-ML, please let me know if you need help resolving the conflicts. |
|
@HugoPBrito I have merged the master branch and have run tests with 100% success, Could you run the CI/CD to enable me to do any remaining fixes required? |
|
Regarding |
Signed-off-by: Legin-ML <leginml2004@gmail.com>
|
Done! |
Context
This PR resolves the issue #10137. This is partially related to the PR #10479.
Description
Adds
--azure-resource-group/--azure-resource-groupsCLI flag to the Azure provider to narrow scans to specific resource groups. When provided, Prowler validates each resource group against all accessible subscriptions via ResourceManagementClient and filters service calls accordingly. Missing resource groups are logged as warnings.Steps to review
prowler azure --az-cli-auth --azure-resource-group rg-prodprowler azure --az-cli-auth --azure-resource-group rg-prod1 rg-prod2prowler azure --az-cli-authprowler azure --az-cli-auth --azure-resource-group invalid-rgshould log a warning and skip itChecklist
Community Checklist
SDK/CLI