Merge with Azure Fallback¶
Overview¶
The merge command now automatically fetches roles from Azure if they are not found in local storage. This creates a seamless workflow for combining permissions from local and Azure roles without requiring explicit load-azure calls for each role.
Changes Made¶
1. New Helper Functions (cli.py)¶
_convert_azure_role_to_local(azure_role: dict) -> AzureRoleDefinition¶
- Converts Azure API response format to local
AzureRoleDefinitionformat - Handles permission block mapping (actions, not_actions, data_actions, not_data_actions)
- Properly determines if a role is custom or built-in based on assignable_scopes
- Reused by: Both
load_azureandmergecommands for consistency
_load_role_from_azure_by_name(name: str, subscription_id: Optional[str]) -> Optional[AzureRoleDefinition]¶
- Searches Azure for a role by name (case-insensitive)
- Returns the converted
AzureRoleDefinitionor None if not found - Gracefully handles errors without throwing exceptions
- Used by:
mergecommand as fallback when local role is not found
2. Updated merge Command¶
New behavior: 1. Tries to load each role from local storage first 2. If not found locally, searches Azure for the role 3. Reports which roles were found locally vs. fetched from Azure 4. Fails only if NO roles can be found (local or Azure)
Output changes:
- When loading from Azure: ℹ Loaded 'RoleName' from Azure
- When roles are not found: ⚠ Roles not found (local or Azure): RoleName1, RoleName2
- Original behavior: ⚠ Role not found: RoleName
Example usage:
> merge --roles "Storage Blob Data Reader"
ℹ Loaded 'Storage Blob Data Reader' from Azure
✓ Merged permissions from 1 role(s)
Mixed local and Azure:
> merge --roles "LocalRole, Reader, Storage Blob Data Reader"
ℹ Loaded 'Reader' from Azure
ℹ Loaded 'Storage Blob Data Reader' from Azure
✓ Merged permissions from 3 role(s)
3. Code Reuse¶
Both load_azure and merge now use the same conversion logic:
- _convert_azure_role_to_local() - Ensures consistent format conversion
- No duplication of Azure role handling logic
- Easier maintenance if Azure response format changes
Benefits¶
- Seamless Workflow: Users can merge roles from Azure without pre-loading them locally
- Flexibility: Supports mixing local and Azure roles in a single merge command
- Better UX: Clear messaging about which roles came from where
- Resilience: Gracefully handles roles that don't exist anywhere (local or Azure)
Test Coverage¶
Added 3 new tests to verify the feature:
1. test_merge_with_azure_fallback - Basic Azure fallback functionality
2. test_merge_mixed_local_and_azure - Mixed local and Azure roles
3. test_merge_azure_role_not_found - Error handling when no roles found
Updated 1 existing test:
- test_merge_with_missing_role_warning - Changed expected message to reflect new behavior
Test Results: 95 tests passing (was 92)
Example Workflow¶
# Set subscription
use-subscription "Development – OI – Software"
# Create a new role
create --name "MyCustomRole" --description "Custom storage role"
# Merge permissions from Azure roles without loading them first
merge --roles "Storage Blob Data Reader, Storage Queue Data Contributor"
# View the merged permissions
show
Requirements Met¶
✅ Merge automatically fetches roles from Azure if not found locally ✅ Clear messaging about which roles came from where ✅ Proper error handling when roles aren't found ✅ Backward compatible - local roles still work as before ✅ Reuses existing filter logic for consistency ✅ All tests passing with no regressions