Skip to content

Commit d9883f1

Browse files
fix: Performance and code quality improvements (v1.2.1)
Performance optimizations and code quality improvements for maintainability: PERFORMANCE: - Replace += operators with ArrayList.Add() for better performance in loops - Optimize array operations in Get-PIMPendingRequests and Get-PIMRolesBatch - Improve memory efficiency for large role collections CODE READABILITY: - Replace backtick line continuations with parameter splatting - Simplify complex Sort-Object statements with variable-based approaches - Improve maintainability of Microsoft Graph API calls MODULE REQUIREMENTS: - Add explicit #Requires statements for Microsoft Graph modules (v2.29.0+) - Declare dependency on Az.Accounts (v5.1.0+) - Ensure consistent module version requirements TECHNICAL DEBT: - Clean up performance anti-patterns identified in code review - Improve overall code maintainability and readability - Maintain backward compatibility while optimizing internals No breaking changes Addresses performance concerns and improves development experience.
1 parent dea0842 commit d9883f1

20 files changed

Lines changed: 1130 additions & 303 deletions

CHANGELOG.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515

1616
---
1717

18+
## [1.2.1] - 2025-08-04
19+
20+
### Added
21+
- **Automatic Dependency Resolution**: Enhanced `Start-PIMActivation` with automatic conflict detection and module installation
22+
- **Force Parameter**: Added `-Force` parameter to `Start-PIMActivation` for fully automated dependency resolution
23+
- **Clean Console Output**: Suppressed verbose output noise while preserving debugging capabilities when requested
24+
- **Module Requirements**: Added explicit `#Requires` statements for all required Microsoft Graph modules and versions
25+
26+
### Fixed
27+
- **Verbose Output Noise**: Suppressed "Populating RepositorySourceLocation" and other unwanted Get-Module verbose messages
28+
- **Function Import Verbosity**: Eliminated verbose function import messages during module loading
29+
- **Console Clutter**: Removed duplicate dependency checking logic and unnecessary output during normal operation
30+
- **Performance**: Replaced `+=` operators with `ArrayList.Add()` for better performance in loops and array operations
31+
- **Code Readability**: Replaced backtick line continuations with parameter splatting for improved maintainability
32+
33+
### Technical Improvements
34+
- **Resolve-PIMDependencies**: New internal function for comprehensive dependency resolution with retry logic
35+
- **Enhanced Error Messages**: Improved user guidance for dependency resolution issues
36+
- **Code Quality**: Performance optimizations including ArrayList usage and cleaner parameter handling
37+
- **Maintainability**: Improved code structure with splatting instead of line continuations
38+
39+
---
40+
1841
## [1.2.0] - 2025-07-31
1942

2043
### Added

PIMActivation.psd1

Lines changed: 27 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
RootModule = 'PIMActivation.psm1'
44

55
# Version number of this module.
6-
ModuleVersion = '1.2.0'
6+
ModuleVersion = '1.2.1'
77

88
# Supported PSEditions - Requires PowerShell Core (7+)
99
CompatiblePSEditions = @('Core')
@@ -26,17 +26,13 @@
2626
# Minimum version of the PowerShell engine required by this module
2727
PowerShellVersion = '7.0'
2828

29-
# Modules that must be imported into the global environment prior to importing this module
30-
RequiredModules = @(
31-
@{ ModuleName = 'Microsoft.Graph.Authentication'; RequiredVersion = '2.29.1' },
32-
@{ ModuleName = 'Microsoft.Graph.Users'; RequiredVersion = '2.29.1' },
33-
@{ ModuleName = 'Microsoft.Graph.Identity.DirectoryManagement'; RequiredVersion = '2.29.1' },
34-
@{ ModuleName = 'Microsoft.Graph.Identity.Governance'; RequiredVersion = '2.29.1' },
35-
@{ ModuleName = 'Az.Accounts'; RequiredVersion = '5.1.0' }
36-
)
29+
# Modules are dynamically installed and imported by the module's initialization logic
30+
RequiredModules = @()
3731

3832
# Functions to export from this module
39-
FunctionsToExport = @('Start-PIMActivation')
33+
FunctionsToExport = @(
34+
'Start-PIMActivation'
35+
)
4036

4137
# Cmdlets to export from this module
4238
CmdletsToExport = @()
@@ -64,113 +60,27 @@
6460

6561
# ReleaseNotes
6662
ReleaseNotes = @'
67-
## Release Notes v1.2.0
68-
69-
### 🚀 Major Performance Enhancements
70-
- **Batch API Operations**: Complete rewrite of role fetching logic using batch operations (85% reduction in API calls)
71-
- **Intelligent Duplicate Role Handling**: Advanced algorithm for managing multiple instances of same role with proper group attribution
72-
- **Enhanced Group-Role Attribution**: Sophisticated cross-referencing system showing which groups provide which roles
73-
- **Comprehensive Error Handling**: Bulletproof property access protection preventing common PowerShell errors
74-
75-
### 🎯 UI/UX Improvements
76-
- **Smooth Progress Flow**: Coordinated progress tracking across all loading phases (no more backwards jumps)
77-
- **Group Visibility**: ProvidedRoles functionality shows exactly which roles each group membership provides
78-
- **Proper Expiration Attribution**: Duplicate roles now show individual expiration times based on their providing groups
79-
- **Enhanced Resource Display**: Shows "Entra ID (via Group: GroupName)" for group-derived roles
80-
81-
### 🔧 Technical Improvements
82-
- **Advanced Array Handling**: @() wrapper implementation preventing .Count property errors
83-
- **Safe Property Access**: PSObject.Properties pattern for bulletproof property checking
84-
- **Intelligent Caching**: Enhanced cache invalidation system with proper timing
85-
- **Defensive Coding**: Comprehensive try-catch blocks around all critical operations
86-
87-
### 🔍 Debugging & Logging
88-
- **Enhanced Verbose Logging**: Detailed progress tracking with differentiated handling for groups vs Entra roles
89-
- **Sophisticated Matching Logic**: Priority-based group assignment with temporal vs permanent preferences
90-
- **Cross-Reference Validation**: Extensive debugging for group-role relationship verification
91-
92-
## Release Notes v1.1.1
93-
94-
### Added
95-
- **Just-in-Time Module Loading**: New `Initialize-PIMModules` system that loads modules only when needed
96-
- **Version Pinning**: Exact module version enforcement to prevent compatibility issues
97-
- **Assembly Conflict Prevention**: Automatic removal of conflicting module versions from session
98-
- Module loading state tracking and compatibility validation
99-
100-
### Changed
101-
- **Updated Module Versions**: Now uses Microsoft.Graph 2.29.1 + Az.Accounts 5.1.0 (tested working combination)
102-
- Replaced legacy `Install-RequiredModules` with new `Initialize-PIMModules` function
103-
- Improved module initialization in `Start-PIMActivation` function
104-
- Updated CI/CD workflow to use latest compatible module versions
105-
106-
### Removed
107-
- **Scripts Folder**: Removed compatibility testing tools (no longer needed with version pinning)
108-
- Legacy module installation and validation code
109-
- Outdated module version requirements
110-
111-
### Fixed
112-
- Resolved `AuthenticateAsync` method signature compatibility issues
113-
- Improved module loading reliability and error handling
114-
- Enhanced troubleshooting guidance for version conflicts
115-
116-
## Release Notes v1.1.0
117-
118-
### ⚡ Major Improvements
119-
- **WAM Authentication**: Implemented Windows Web Account Manager (WAM) for reliable authentication
120-
- **Removed MSAL.PS Dependency**: Now uses direct MSAL.NET calls for better reliability and performance
121-
- **Enhanced Authentication Context**: Improved handling of conditional access policies
122-
123-
### 🔧 Technical Changes
124-
- Direct integration with Az.Accounts MSAL assemblies
125-
- Eliminated PowerShell 5.1 fallback - now fully PowerShell 7+ native
126-
- Improved error handling and timeout management
127-
- Better assembly loading and management
128-
129-
## Release Notes v1.0.1
130-
131-
### 🔧 Bug Fixes
132-
- Fixed authentication context token acquisition for conditional access policies
133-
- Enhanced error handling for authentication scenarios
134-
- Improved MSAL.PS integration for more reliable interactive authentication prompts
135-
- Fixed timing issues with authentication context token validation
136-
137-
### 🆕 New Features
138-
- Added token caching to minimize re-authentication prompts
139-
- Enhanced authentication context flow with better error messages
140-
- Improved handling of authentication timeouts and cancellation
141-
142-
### 🔧 Technical Changes
143-
- Better integration with MSAL.PS for authentication context scenarios
144-
- Enhanced token validation and refresh logic
145-
- Improved error handling for authentication context failures
146-
147-
## Release Notes v1.0.0
148-
149-
### 🎉 Initial Release
150-
- **Modern GUI Interface**: Clean Windows Forms application for PIM role management
151-
- **Multi-Role Support**: Activate Microsoft Entra ID roles and PIM-enabled security groups
152-
- **Authentication Context**: Seamless handling of Conditional Access authentication context policies
153-
- **Bulk Operations**: Select and activate multiple roles simultaneously with policy validation
154-
- **PowerShell Compatibility**: Requires PowerShell 7+ for optimal performance and modern language features
155-
- **Policy Compliance**: Automatic detection of MFA, justification, and ticket requirements
156-
- **Real-time Updates**: Live monitoring of active assignments and pending requests
157-
158-
### 🔧 Technical Features
159-
- Direct REST API calls for authentication context preservation
160-
- Automatic module dependency management
161-
- Comprehensive error handling and user feedback
162-
163-
### 📋 Requirements
164-
- Windows Operating System
165-
- PowerShell 7+ (Download from https://aka.ms/powershell)
166-
- Microsoft Graph PowerShell modules (auto-installed)
167-
- Az.Accounts module for WAM authentication support
168-
- Appropriate Entra ID permissions for PIM role management
169-
170-
### 📝 Development Note
171-
This module was developed with the assistance of AI tools (GitHub Copilot and Claude), combining AI-accelerated development with human expertise in Microsoft identity and security workflows.
172-
173-
For detailed usage instructions, see the README.md file.
63+
## PIMActivation v1.2.1
64+
65+
### � What's New
66+
- Enhanced automatic dependency resolution
67+
- Added -Force parameter for fully automated setup
68+
- Cleaner console output with reduced verbose noise
69+
- Improved error handling and user guidance
70+
71+
### � Full Release Notes
72+
For complete release notes, changelog, and detailed information:
73+
- **GitHub Releases**: https://github.com/Noble-Effeciency13/PIMActivation/releases
74+
- **Changelog**: https://github.com/Noble-Effeciency13/PIMActivation/blob/main/CHANGELOG.md
75+
- **Documentation**: https://github.com/Noble-Effeciency13/PIMActivation/blob/main/README.md
76+
77+
### � Getting Started
78+
```powershell
79+
Install-Module PIMActivation -Scope CurrentUser
80+
Start-PIMActivation
81+
```
82+
83+
PowerShell module for Microsoft Entra ID Privileged Identity Management (PIM) role activations through a modern GUI interface.
17484
'@
17585
# Flag to indicate whether the module requires explicit user acceptance
17686
RequireLicenseAcceptance = $false

PIMActivation.psm1

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,32 @@
11
#Requires -Version 7.0
2+
#Requires -Modules @{
3+
ModuleName = 'Microsoft.Graph.Authentication'
4+
ModuleVersion = '2.29.0'
5+
}
6+
#Requires -Modules @{
7+
ModuleName = 'Microsoft.Graph.Users'
8+
ModuleVersion = '2.29.0'
9+
}
10+
#Requires -Modules @{
11+
ModuleName = 'Microsoft.Graph.Identity.DirectoryManagement'
12+
ModuleVersion = '2.29.0'
13+
}
14+
#Requires -Modules @{
15+
ModuleName = 'Microsoft.Graph.Identity.Governance'
16+
ModuleVersion = '2.29.0'
17+
}
18+
#Requires -Modules @{
19+
ModuleName = 'Microsoft.Graph.Groups'
20+
ModuleVersion = '2.29.0'
21+
}
22+
#Requires -Modules @{
23+
ModuleName = 'Microsoft.Graph.Identity.SignIns'
24+
ModuleVersion = '2.29.0'
25+
}
26+
#Requires -Modules @{
27+
ModuleName = 'Az.Accounts'
28+
ModuleVersion = '5.1.0'
29+
}
230

331
# Set strict mode for better error handling
432
Set-StrictMode -Version Latest
@@ -69,10 +97,12 @@ $script:AuthContextCompletionTime = $null
6997
# Module loading state for just-in-time loading
7098
$script:ModuleLoadingState = @{}
7199
$script:RequiredModuleVersions = @{
72-
'Microsoft.Graph.Authentication' = '2.29.1'
73-
'Microsoft.Graph.Users' = '2.29.1'
74-
'Microsoft.Graph.Identity.DirectoryManagement' = '2.29.1'
75-
'Microsoft.Graph.Identity.Governance' = '2.29.1'
100+
'Microsoft.Graph.Authentication' = '2.29.0'
101+
'Microsoft.Graph.Users' = '2.29.0'
102+
'Microsoft.Graph.Identity.DirectoryManagement' = '2.29.0'
103+
'Microsoft.Graph.Identity.Governance' = '2.29.0'
104+
'Microsoft.Graph.Groups' = '2.29.0'
105+
'Microsoft.Graph.Identity.SignIns' = '2.29.0'
76106
'Az.Accounts' = '5.1.0'
77107
}
78108

@@ -81,26 +111,29 @@ $script:RequiredModuleVersions = @{
81111
#region Import Functions
82112

83113
# Import all functions from subdirectories
84-
$functionFolders = @(
114+
$functionFolders = [System.Collections.ArrayList]::new()
115+
$null = $functionFolders.AddRange(@(
85116
'Authentication',
86117
'RoleManagement',
87118
'UI',
88119
'Utilities'
89-
)
120+
))
90121

91122
# Note: Profiles folder contains placeholder functions for planned features
92-
$functionFolders += 'Profiles'
123+
$null = $functionFolders.Add('Profiles')
93124

94125
# Import private functions from organized folders
126+
# Temporarily suppress verbose output during function imports to reduce noise
127+
$originalVerbosePreference = $VerbosePreference
128+
$VerbosePreference = 'SilentlyContinue'
129+
95130
foreach ($folder in $functionFolders) {
96131
$folderPath = Join-Path -Path "$script:ModuleRoot\Private" -ChildPath $folder
97132
if (Test-Path -Path $folderPath) {
98-
Write-Verbose "Importing functions from $folder"
99133
$functions = Get-ChildItem -Path $folderPath -Filter '*.ps1' -File -ErrorAction SilentlyContinue
100134

101135
foreach ($function in $functions) {
102136
try {
103-
Write-Verbose "Importing $($function.Name)"
104137
. $function.FullName
105138
}
106139
catch {
@@ -114,7 +147,6 @@ foreach ($folder in $functionFolders) {
114147
$privateRoot = Get-ChildItem -Path "$script:ModuleRoot\Private" -Filter '*.ps1' -File -ErrorAction SilentlyContinue
115148
foreach ($import in $privateRoot) {
116149
try {
117-
Write-Verbose "Importing $($import.Name)"
118150
. $import.FullName
119151
}
120152
catch {
@@ -126,14 +158,16 @@ foreach ($import in $privateRoot) {
126158
$Public = @(Get-ChildItem -Path "$script:ModuleRoot\Public" -Filter '*.ps1' -File -ErrorAction SilentlyContinue)
127159
foreach ($import in $Public) {
128160
try {
129-
Write-Verbose "Importing public function $($import.Name)"
130161
. $import.FullName
131162
}
132163
catch {
133164
Write-Error -Message "Failed to import function $($import.FullName): $_"
134165
}
135166
}
136167

168+
# Restore original verbose preference
169+
$VerbosePreference = $originalVerbosePreference
170+
137171
#endregion Import Functions
138172

139173
#region Export Module Members
@@ -143,6 +177,14 @@ Export-ModuleMember -Function $Public.BaseName -Alias *
143177

144178
#endregion Export Module Members
145179

180+
#region Module Initialization
181+
182+
# Dependencies are loaded on-demand when Start-PIMActivation is called
183+
# This ensures clean module loading and avoids import-time dependency issues
184+
# All dependency management is handled automatically by Start-PIMActivation
185+
186+
#endregion Module Initialization
187+
146188
#region Cleanup
147189

148190
# Clean up variables

Private/Authentication/Clear-AuthenticationCache.ps1

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,9 @@ function Clear-AuthenticationCache {
5959
$edgeCookies = Join-Path $env:LOCALAPPDATA "Microsoft\Edge\User Data\Default\Cookies"
6060
$chromeCookies = Join-Path $env:LOCALAPPDATA "Google\Chrome\User Data\Default\Cookies"
6161

62-
$browserPaths = @()
63-
if (Test-Path $edgeCookies) { $browserPaths += "Edge" }
64-
if (Test-Path $chromeCookies) { $browserPaths += "Chrome" }
62+
$browserPaths = [System.Collections.ArrayList]::new()
63+
if (Test-Path $edgeCookies) { $null = $browserPaths.Add("Edge") }
64+
if (Test-Path $chromeCookies) { $null = $browserPaths.Add("Chrome") }
6565

6666
if ($browserPaths.Count -gt 0) {
6767
Write-Verbose "Browser cookie stores detected for: $($browserPaths -join ', ')"

0 commit comments

Comments
 (0)