-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathExport-LPMSPermissionAnalysisReport.ps1
More file actions
316 lines (259 loc) · 14 KB
/
Export-LPMSPermissionAnalysisReport.ps1
File metadata and controls
316 lines (259 loc) · 14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
function Export-LPMSPermissionAnalysisReport {
<#
.SYNOPSIS
Generates an interactive HTML report for Microsoft Graph permission analysis results.
.DESCRIPTION
This function creates a comprehensive, interactive HTML report that visualizes permission analysis
results for Microsoft Graph applications. The report provides a user-friendly interface for reviewing
permission optimization opportunities, API activity patterns, and throttling statistics.
The generated report features:
- **Dark/Light Mode Toggle**: User preference for viewing comfort
- **Interactive Data Tables**: Sortable and filterable tables for all data views
- **Permission Status Indicators**: Color-coded badges showing optimal, excess, and missing permissions
- **Throttling Statistics**: Visual severity indicators and detailed metrics
- **Detailed Modal Views**: Click any application for comprehensive details
- **CSV Export**: Export filtered data for further analysis
- **Search and Filter**: Real-time filtering across all applications
- **Responsive Design**: Works seamlessly on desktop and mobile devices
- **Offline Capable**: Once generated, works without internet connection
Report Sections:
1. **Summary Statistics**: Overview of total apps, permissions, and coverage
2. **Applications Table**: Sortable list with key metrics per application
3. **Application Details**: Click any row to see:
- Current vs. optimal permission comparison
- API activity breakdown with method and endpoint details
- Throttling statistics and health indicators
- Permission recommendations
The function processes application analysis data (typically from Get-LPMSPermissionAnalysis),
embeds it as JSON into an HTML template with Tailwind CSS styling, and generates a
standalone HTML file that can be shared, archived, or viewed in any modern browser.
Use Cases:
- **Security Audits**: Document permission analysis for compliance requirements
- **Permission Right-Sizing**: Identify and communicate permission optimization opportunities
- **Change Management**: Create before/after reports for permission changes
- **Executive Reporting**: Visual, easy-to-understand permission health dashboard
- **Historical Tracking**: Archive reports over time to track permission hygiene trends
.PARAMETER AppData
An array of application permission analysis objects, typically from Get-LPMSPermissionAnalysis.
This parameter accepts pipeline input, allowing multiple applications to be processed efficiently.
Each object should contain the following properties:
Required Properties:
- **PrincipalName** (String): The display name of the application or service principal
- **PrincipalId** (String): The Azure AD object ID of the service principal
- **CurrentPermissions** (Array): Currently assigned Microsoft Graph permissions (strings)
- **OptimalPermissions** (Array): Optimal permission objects with:
* Permission (String): The permission name
* ActivitiesCovered (Int): Number of activities this permission covers
- **ExcessPermissions** (Array): Permissions assigned but not needed based on activity
- **RequiredPermissions** (Array): Permissions needed but not currently assigned
- **Activity** (Array): API activity objects with:
* Method (String): HTTP method (GET, POST, etc.)
* Uri (String): API endpoint called
- **UnmatchedActivities** (Array): Activities that couldn't be matched to permission maps
- **MatchedAllActivity** (Boolean): True if all activities were successfully matched
- **AppRoleCount** (Int): Total number of Microsoft Graph app roles assigned
Optional Properties:
- **ThrottlingStats** (PSCustomObject): Throttling statistics with:
* TotalRequests (Int): Total API requests
* Total429Errors (Int): Throttling error count
* ThrottleRate (Double): Percentage of throttled requests
* ThrottlingStatus (String): Severity classification (Normal, Warning, Critical, etc.)
.PARAMETER OutputPath
The file path where the HTML report will be saved. Can be absolute or relative path.
Default: ".\PermissionAnalysisReport.html" (current directory)
Tips:
- Use timestamped filenames for historical tracking
- Ensure the directory exists or the function will fail
- .html extension is recommended for browser association
.PARAMETER ReportTitle
The title displayed in the report header and browser tab. This appears at the top of
the report and in the browser's title bar/tab.
Default: "Microsoft Graph Permission Analysis Report"
Use Cases:
- Include environment: "Production Apps - Graph Permissions"
- Add time period: "Q4 2024 Permission Analysis"
- Specify department: "HR Department - Graph API Audit"
.OUTPUTS
System.String
Returns the full absolute path to the generated HTML report file.
This allows for easy chaining with other commands like Start-Process to open the report,
or copy operations to move it to a shared location.
.EXAMPLE
$results = Get-LPMSPermissionAnalysis -WorkspaceId $wsId -Days 30
$reportPath = Export-LPMSPermissionAnalysisReport -AppData $results -OutputPath ".\reports\monthly-analysis.html"
Start-Process $reportPath
Description:
Analyzes 30 days of activity, generates a report, and immediately opens it in the default browser.
.EXAMPLE
Get-LPMSPermissionAnalysis -WorkspaceId $wsId -Days 90 -ApplicationId $criticalApps |
Export-LPMSPermissionAnalysisReport -OutputPath "C:\Reports\Critical_Apps_$(Get-Date -Format 'yyyyMMdd').html" -ReportTitle "Critical Applications - Q4 Analysis"
Description:
Analyzes specific critical applications over 90 days, generates a timestamped report
with a custom title, saved to a dedicated reports folder.
.EXAMPLE
# Compare current vs. previous month
$thisMonth = Get-LPMSPermissionAnalysis -WorkspaceId $wsId -Days 30
$lastMonth = Get-LPMSPermissionAnalysis -WorkspaceId $wsId -Days 60 | Where-Object {
$_.LastActivityDate -lt (Get-Date).AddDays(-30)
}
Export-LPMSPermissionAnalysisReport -AppData $thisMonth -OutputPath ".\reports\current-month.html" -ReportTitle "Current Month"
Export-LPMSPermissionAnalysisReport -AppData $lastMonth -OutputPath ".\reports\last-month.html" -ReportTitle "Previous Month"
Description:
Generates two separate reports for comparison between time periods to identify
permission drift or changes in application behavior.
.EXAMPLE
# Filter and report on problematic applications only
$allResults = Get-LPMSPermissionAnalysis -WorkspaceId $wsId -Days 30
$problematic = $allResults | Where-Object {
$_.ExcessPermissions.Count -gt 5 -or
$_.RequiredPermissions.Count -gt 0 -or
$_.ThrottlingStats.ThrottlingSeverity -ge 3
}
if ($problematic.Count -gt 0) {
$report = Export-LPMSPermissionAnalysisReport -AppData $problematic -OutputPath ".\ActionRequired.html" -ReportTitle "Applications Requiring Attention"
"Found $($problematic.Count) applications requiring attention. Report: $report"
} else {
"All applications are optimally configured!"
}
Description:
Filters analysis results to only applications with significant issues (excess permissions,
missing permissions, or throttling), generating a focused action-required report.
.NOTES
Prerequisites:
- PowerShell 5.1 or later
- Module must be properly installed with data\base.html template file
- Write permissions to the output directory
Template Architecture:
- The function uses a Jinja2-style template with placeholder blocks
- Template location: <module-root>\data\base.html
- Placeholders replaced:
* {% block app_data %}{% endblock %} -> JSON data
* {% block title %}{% endblock %} -> Report title
* {% block generated_on %}{% endblock %} -> Generation timestamp
JSON Processing:
- Application data converted to JSON with depth 10 (preserves nested structures)
- Special characters properly escaped for JavaScript embedding:
* Backslashes: \ -> \\
* Quotes: " -> \"
* Newlines: \n -> \\n
- Data is compressed (no indentation) to reduce file size
Report Size Considerations:
- Typical report: 500KB - 2MB depending on number of applications
- Large deployments (100+ apps): May reach 5-10MB
- Consider splitting very large tenants into multiple reports
- All data embedded in single HTML file (no external dependencies)
Browser Compatibility:
- Requires modern browser (released within last 2 years):
* Chrome/Edge: Version 90+
* Firefox: Version 88+
* Safari: Version 14+
- JavaScript must be enabled
- Works fully offline once generated
- No server-side processing required
Security Considerations:
- Reports may contain sensitive information (app names, permission details)
- Store reports securely with appropriate access controls
- Consider encrypting archived reports
- Sanitize application names if sharing externally
- No credentials or secrets are included in reports
Performance:
- Report generation: ~1-2 seconds for 50 applications
- Browser rendering: ~2-5 seconds for 100 applications
- Search/filter: Real-time (< 100ms for typical datasets)
- Large datasets (500+ apps) may cause slower browser performance
Troubleshooting:
If "Template file not found" error:
- Verify module is properly installed: Get-Module LeastPrivilegedMSGraph -ListAvailable
- Check module path: (Get-Module LeastPrivilegedMSGraph).ModuleBase
- Ensure data\base.html exists in module directory
- Reinstall module if template is missing
If report displays incorrectly:
- Clear browser cache and reload
- Try a different browser
- Check browser console for JavaScript errors (F12)
- Verify the HTML file isn't corrupted (should be valid UTF-8)
If data appears truncated:
- Check that ConvertTo-Json depth (15) is sufficient for nested data
- Review input data structure for unexpected nesting
- Consider simplifying input data if depth exceeds 15 levels
Common Use Cases:
1. **Monthly Security Reviews**: Generate reports for security team review
2. **Audit Documentation**: Create compliance documentation for auditors
3. **Change Requests**: Attach reports to permission change requests
4. **Trend Analysis**: Archive monthly reports to track improvements
5. **Stakeholder Communication**: Share visual reports with management
6. **Incident Response**: Document permission state during investigations
Best Practices:
- Use consistent naming conventions for output files
- Include timestamps for historical tracking
- Store reports in version-controlled or backed-up locations
- Review reports regularly (weekly/monthly depending on environment)
- Act on recommendations within 30 days
- Archive reports for compliance (typically 1-7 years retention)
.LINK
https://learn.microsoft.com/en-us/graph/permissions-reference
.LINK
https://mynster9361.github.io/Least_Privileged_MSGraph/commands/Export-LPMSPermissionAnalysisReport.html
#>
[CmdletBinding()]
[OutputType([System.String])]
param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[array]$AppData,
[Parameter(Mandatory = $false)]
[string]$OutputPath = ".\PermissionAnalysisReport.html",
[Parameter(Mandatory = $false)]
[string]$ReportTitle = "Microsoft Graph Permission Analysis Report"
)
begin {
$allAppData = @()
}
process {
# Accumulate all pipeline input
$allAppData += $AppData
}
end {
Write-PSFMessage -Level Debug -Message "Total apps received: $($allAppData.Count)"
# Convert accumulated data to JSON for embedding
$jsonData = $allAppData | ConvertTo-Json -Depth 15 -Compress
# Properly escape for JavaScript - need to escape backslashes and quotes
$jsonData = $jsonData.Replace('\', '\\').Replace('"', '\"').Replace([Environment]::NewLine, '\n')
# Get the module root directory (handles versioned paths correctly)
$script:moduleRoot = $PSScriptRoot
# Load the HTML template
$templatePath = Join-Path -Path $script:moduleRoot -ChildPath "data\base.html"
Write-PSFMessage -Level Debug -Message "Module root: $script:moduleRoot"
Write-PSFMessage -Level Debug -Message "Template path: $templatePath"
if (-not (Test-Path -Path $templatePath)) {
throw "Template file not found: $templatePath"
}
# Determine which Graph service to use for tenant info
$tokenInfo = (Get-EntraToken).Service
if ($tokenInfo -contains "GraphBeta") {
$serviceToUse = "GraphBeta"
}
else {
$serviceToUse = "Graph"
}
# Retrieve tenant information
try {
$tenantInfo = Invoke-EntraRequest -Path "/organization" -Method GET -Service $serviceToUse
}
catch {
Write-PSFMessage -Level Warning -Message "Failed to retrieve tenant information: $_"
$tenantInfo = @{ id = "Unknown"; displayName = "Unknown" }
}
$html = Get-Content -Path $templatePath -Raw
$html = $html -replace '{% block app_data %}{% endblock %}', $jsonData
$html = $html -replace '{% block title %}{% endblock %}', $ReportTitle
$html = $html -replace '{% block title %}{% endblock %}', $ReportTitle
$html = $html -replace '{% block tenant_id %}{% endblock %}', $tenantInfo.id
$html = $html -replace '{% block tenant_name %}{% endblock %}', $tenantInfo.displayName
$html = $html -replace '{% block generated_on %}{% endblock %}', (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
# Write the HTML to file
$html | Out-File -FilePath $OutputPath -Encoding UTF8
"Report generated successfully: $OutputPath"
"Total applications in report: $($allAppData.Count)"
return $OutputPath
}
}