Skip to content

feat(azure): filtering scans at resource group level#10657

Draft
Legin-ML wants to merge 23 commits intoprowler-cloud:masterfrom
Legin-ML:feat/azure-rg-filtering
Draft

feat(azure): filtering scans at resource group level#10657
Legin-ML wants to merge 23 commits intoprowler-cloud:masterfrom
Legin-ML:feat/azure-rg-filtering

Conversation

@Legin-ML
Copy link
Copy Markdown

Context

This PR resolves the issue #10137. This is partially related to the PR #10479.

Description

Adds --azure-resource-group/--azure-resource-groups CLI 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

  1. Scan with a single resource group: prowler azure --az-cli-auth --azure-resource-group rg-prod
  2. Scan with multiple resource groups: prowler azure --az-cli-auth --azure-resource-group rg-prod1 rg-prod2
  3. Verify fallback to full scan when flag is omitted: prowler azure --az-cli-auth
  4. Verify warning on invalid resource group(s): prowler azure --az-cli-auth --azure-resource-group invalid-rg should log a warning and skip it
  5. Run tests: (1/22 services covered under test currently)

Checklist

Community Checklist
  • This feature/issue is listed in here
  • Is it assigned to me.

SDK/CLI

  • Are there new checks included in this PR? : No

@github-actions github-actions Bot added documentation provider/azure Issues/PRs related with the Azure provider labels Apr 13, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 13, 2026

Conflict Markers Resolved

All conflict markers have been successfully resolved in this pull request.

@github-actions github-actions Bot added the community Opened by the Community label Apr 13, 2026
Signed-off-by: Legin-ML <leginml2004@gmail.com>
Copy link
Copy Markdown
Member

@HugoPBrito HugoPBrito left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  • network can produce false FAIL results because Network Watcher resources are filtered by the watcher's own resource group while the downstream checks still evaluate subscription-wide coverage.
  • monitor can produce false FAIL results 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:

  1. Classify Azure checks as RG-scoped, subscription/tenant-scoped, or cross-scope.
  2. For subscription-scoped or cross-scope checks that become ambiguous under RG filtering, skip them with a warning instead of returning a FAIL.
  3. Make RG validation case-insensitive and update the docs accordingly.
  4. Add targeted tests for NetworkWatcherRG, activity log alerts living in a different resource group, and valid RG names provided with different casing.

Comment thread prowler/providers/azure/services/network/network_service.py Outdated
Comment thread prowler/providers/azure/services/monitor/monitor_service.py Outdated
Comment thread prowler/providers/azure/azure_provider.py Outdated
@Legin-ML
Copy link
Copy Markdown
Author

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
Copy link
Copy Markdown

codecov Bot commented Apr 27, 2026

Codecov Report

❌ Patch coverage is 47.01398% with 417 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.77%. Comparing base (1de01bc) to head (f319e67).
⚠️ Report is 57 commits behind head on master.

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     
Flag Coverage Δ
prowler-py3.10-azure ?
prowler-py3.11-azure 81.77% <47.01%> (-4.41%) ⬇️
prowler-py3.12-azure 81.77% <47.01%> (-4.41%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
prowler 81.77% <47.01%> (-4.41%) ⬇️
api ∅ <ø> (∅)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@HugoPBrito
Copy link
Copy Markdown
Member

Thanks @Legin-ML! Regarding tests:

Yes, please add coverage for all 22 services — the current 1/22 is what's making Codecov fail (patch at 29.03%). For each service test, the key cases to exercise are:

  1. No RG filter → service returns the full set of resources (current behavior, regression guard).
  2. Single RG filter → only resources in that RG are returned.
  3. Multiple RG filter → union across the provided RGs.
  4. Mixed-case RG → matches Azure's case-insensitive semantics (once you apply the normalization fix in azure_provider.py).

On top of that, please add the targeted cases for the ambiguous subscription-scoped checks:

  • network_watcher_* checks return MANUAL when NetworkWatcherRG is outside the filtered RGs.
  • monitor.activity_log_alerts checks return MANUAL when the alert lives in a different RG than the one being scanned.

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 azure_resource_groups=[...]" doesn't exist yet, feel free to add one under tests/providers/azure/azure_fixtures.py so the 22 service tests stay DRY.

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>
@Legin-ML Legin-ML force-pushed the feat/azure-rg-filtering branch from 2e27af9 to b86c49f Compare April 29, 2026 06:55
Legin-ML and others added 2 commits April 29, 2026 12:34
@Legin-ML Legin-ML force-pushed the feat/azure-rg-filtering branch from ee4230a to 73363f9 Compare April 30, 2026 09:18
@Legin-ML
Copy link
Copy Markdown
Author

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 .lower() guard while comparing the RGs @HugoPBrito. 3 checks which consume network_watcher and 11 checks which consume activity_log_alerts have been set to manual.

Copy link
Copy Markdown
Member

@HugoPBrito HugoPBrito left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

  1. validate_resource_groups can take down provider initialization if a single subscription fails (permissions/throttling). [comment on azure_provider.py:1091]
  2. Type annotations declare list for what is actually dict[str, list] — misleads readers and breaks type checkers. [comments on azure_provider.py:107 and :348]
  3. print_credentials shows [] (instead of ALL or a clearer message) when no RG matches — the user sees an apparently "empty" scan with no signal. [comment on azure_provider.py:467]
  4. 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-group is used. Monitor and Network checks do emit a MANUAL finding with status_extended. We need to homogenize so the user never loses visibility. [comments on entra_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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread prowler/providers/azure/azure_provider.py
Comment thread prowler/providers/azure/azure_provider.py Outdated
Comment thread prowler/providers/azure/azure_provider.py Outdated
Comment thread prowler/providers/azure/lib/service/service.py
Comment thread prowler/providers/azure/services/aisearch/aisearch_service.py Outdated
Comment thread prowler/providers/azure/services/monitor/monitor_service.py
Comment thread prowler/providers/azure/services/entra/entra_service.py
@Legin-ML
Copy link
Copy Markdown
Author

Legin-ML commented May 6, 2026

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 😊

Copy link
Copy Markdown
Member

@HugoPBrito HugoPBrito left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

  1. All 11 monitor activity_log_alerts checks now produce zero findings when --azure-resource-group is active.
    monitor_service.get_alert_rules early-returns {} (monitor_service.py:76-77), and every monitor alert check iterates monitor_client.alert_rules.items() — e.g. monitor_alert_create_update_nsg.py:13. With an empty dict, the for loop 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.subscriptions instead of monitor_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.
  2. tests/providers/azure/azure_provider_test.py::test_validate_resource_groups_mixed_case is 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 the existing_rgs.list() mock was called only once per subscription (regression guard).

Important

  1. validate_resource_groups is still annotated -> dict: at azure_provider.py:1091. The property is now dict[str, list[str]]; the function should match for consistency and to lock in the contract.

  2. 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.

Legin-ML added 2 commits May 7, 2026 12:17
Signed-off-by: Legin-ML <leginml2004@gmail.com>
Signed-off-by: Legin-ML <leginml2004@gmail.com>
@Legin-ML
Copy link
Copy Markdown
Author

Legin-ML commented May 7, 2026

Hi @HugoPBrito,

  1. I have modified the monitor checks and tested that they produce a MANUAL output instead of exiting silently.
  2. Fixed the mixed case test case (It seems I only ran the tests under tests/providers/azure/services before 😅 )
  3. Modified the annotation and docs.

@HugoPBrito
Copy link
Copy Markdown
Member

HugoPBrito commented May 7, 2026

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.

@HugoPBrito
Copy link
Copy Markdown
Member

A big PR was just merged. @Legin-ML, please let me know if you need help resolving the conflicts.

@Legin-ML Legin-ML changed the title Azure - Filtering scans at resource group level feat(azure): filtering scans at resource group level May 8, 2026
@Legin-ML
Copy link
Copy Markdown
Author

Legin-ML commented May 8, 2026

@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?

@HugoPBrito
Copy link
Copy Markdown
Member

Regarding Duplicate Test Names, please rename Azure iam_service_test.py to azure_iam_service_test.py.

Signed-off-by: Legin-ML <leginml2004@gmail.com>
@Legin-ML
Copy link
Copy Markdown
Author

Legin-ML commented May 8, 2026

Done!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community Opened by the Community documentation provider/azure Issues/PRs related with the Azure provider

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants