feat: suggest Azure built-in roles for required permissions (#36) - #300
Open
Brian Gordon Davis (bgdnext64) wants to merge 2 commits into
Open
feat: suggest Azure built-in roles for required permissions (#36)#300Brian Gordon Davis (bgdnext64) wants to merge 2 commits into
Brian Gordon Davis (bgdnext64) wants to merge 2 commits into
Conversation
Add a --suggestRoles flag that, after MPF computes the minimum permissions, queries Azure built-in role definitions and suggests role(s) covering them. - domain: SuggestBuiltInRoles with wildcard-aware matching, NotActions handling, least-privilege breadth-score ranking, and greedy minimal set cover - infrastructure: RoleDefinitionManager fetches built-in roles via RoleDefinitionsClient - presentation: text and JSON formatting of suggestions - wired into arm, bicep, and terraform commands - unit tests for matching/ranking and formatter; opt-in read-only Azure integration test
Copilot started reviewing on behalf of
Brian Gordon Davis (bgdnext64)
August 4, 2026 16:41
View session
Contributor
There was a problem hiding this comment.
Pull request overview
Adds an opt-in “built-in role suggestion” feature to MPF that, after discovering minimum required permissions, fetches Azure built-in role definitions and suggests least-privilege role(s) (single-role matches, greedy combinations, and uncovered permissions) in text or JSON form.
Changes:
- Introduces domain logic to match required permissions to built-in roles (wildcard + NotActions support) and rank suggestions by specificity, with unit tests.
- Adds infrastructure for enumerating Azure built-in role definitions and wires role suggestion into ARM/Bicep/Terraform commands behind
--suggestRoles. - Adds presentation formatting (text + JSON) and documents the new CLI flag/environment variable.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/usecase/roleSuggester.go | Defines BuiltInRoleProvider interface used for role enumeration. |
| pkg/domain/roleSuggestion.go | Implements role matching, greedy set cover, and specificity scoring. |
| pkg/domain/roleSuggestion_test.go | Unit tests for wildcard matching, ordering, combinations, and edge cases. |
| pkg/infrastructure/azureAPI/azureApiClient.go | Adds RoleDefinitionsClient initialization for role enumeration. |
| pkg/infrastructure/roleDefinitionManager/roleDefinitionManager.go | Implements built-in role listing and conversion into domain model. |
| pkg/infrastructure/roleDefinitionManager/roleDefinitionManager_integration_test.go | Read-only integration test for listing built-in roles and running suggestion. |
| pkg/presentation/roleSuggestionFormatter.go | Adds text/JSON formatting for role suggestions. |
| pkg/presentation/roleSuggestionFormatter_test.go | Tests for text output cases, JSON output, and single-match capping. |
| cmd/rootCmd.go | Adds --suggestRoles persistent flag. |
| cmd/roleSuggestion.go | Implements end-to-end “fetch → suggest → display” flow for role suggestions. |
| cmd/armCmd.go | Calls role suggestion after displaying ARM MPF results. |
| cmd/bicepCmd.go | Calls role suggestion after displaying Bicep MPF results. |
| cmd/terraformCmd.go | Calls role suggestion after displaying Terraform MPF results. |
| docs/commandline-flags-and-env-variables.md | Documents --suggestRoles / MPF_SUGGESTROLES and behavior. |
Comment on lines
+60
to
+64
| suggestion := domain.SuggestBuiltInRoles(requiredPermissions, builtInRoles) | ||
|
|
||
| if err := presentation.DisplayRoleSuggestion(os.Stdout, suggestion, flgJSONOutput); err != nil { | ||
| log.Errorf("Error displaying role suggestion: %v", err) | ||
| } |
Comment on lines
+284
to
+306
| func actionMatchesPattern(pattern string, action string) bool { | ||
| if pattern == "" { | ||
| return false | ||
| } | ||
| if !strings.Contains(pattern, "*") { | ||
| return strings.EqualFold(pattern, action) | ||
| } | ||
|
|
||
| var sb strings.Builder | ||
| sb.WriteString("(?i)^") | ||
| for _, segment := range strings.Split(pattern, "*") { | ||
| sb.WriteString(regexp.QuoteMeta(segment)) | ||
| sb.WriteString(".*") | ||
| } | ||
| // Remove the trailing ".*" added after the final segment and anchor the end. | ||
| regexStr := strings.TrimSuffix(sb.String(), ".*") + "$" | ||
|
|
||
| re, err := regexp.Compile(regexStr) | ||
| if err != nil { | ||
| return false | ||
| } | ||
| return re.MatchString(action) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements #36 — suggest Azure built-in role(s) that cover the minimum permissions discovered by MPF.
After MPF determines the minimum required permissions for a deployment, this feature optionally matches those permissions against the subscription's Azure built-in role definitions and prints:
The feature is opt-in via a new global flag
--suggestRoles(envMPF_SUGGESTROLES) and works across thearm,bicep, andterraformsubcommands. Output honors the existing--jsonOutputflag.Behavior
*, e.g. Owner/Contributor) are deprioritized and rank last.Microsoft.Storage/*) are matched case-insensitively against required permissions, andNotActionsexclusions are respected.Changes
pkg/domain/roleSuggestion.go— pure role-matching and ranking logic (wildcard matching, greedy set cover, specificity scoring) with unit tests.pkg/usecase/roleSuggester.go—BuiltInRoleProviderinterface.pkg/infrastructure/roleDefinitionManager/— fetches built-in role definitions viaRoleDefinitionsClient, with an opt-in read-only integration test.pkg/infrastructure/azureAPI/azureApiClient.go— adds and initializesRoleDefinitionsClient.pkg/presentation/roleSuggestionFormatter.go— text and JSON output, with tests.cmd/—--suggestRolesflag and wiring into the arm/bicep/terraform commands.docs/commandline-flags-and-env-variables.md— documents the new flag.Testing
go build ./...,go vet ./..., and the full unit suite pass.Closes #36