Public/Invoke-LicenseOptimization.ps1
|
function Invoke-LicenseOptimization { <# .SYNOPSIS Orchestrates a full Microsoft 365 license optimization analysis. .DESCRIPTION Runs all license analysis functions (inventory, underutilization, inactive users, savings report) and generates an HTML dashboard. Provides a comprehensive view of license waste and savings opportunities. .PARAMETER OutputPath Directory where the HTML report will be saved. Defaults to current directory. .PARAMETER DaysInactive Number of days without sign-in to flag as inactive. Default: 90. .PARAMETER IncludeGuests Include guest (external) users in the inactive user analysis. .EXAMPLE Invoke-LicenseOptimization -OutputPath "C:\Reports" -DaysInactive 60 Runs full analysis and saves the HTML dashboard to C:\Reports. .EXAMPLE Invoke-LicenseOptimization -IncludeGuests Runs analysis including guest users and saves the report to the current directory. .OUTPUTS System.IO.FileInfo - the generated HTML report file #> [CmdletBinding()] param( [Parameter(Mandatory = $false)] [string]$OutputPath = (Get-Location).Path, [Parameter(Mandatory = $false)] [ValidateRange(1, 365)] [int]$DaysInactive = 90, [Parameter(Mandatory = $false)] [switch]$IncludeGuests ) $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() Write-Host "" Write-Host "M365 License Optimizer" -ForegroundColor DarkYellow Write-Host "======================" -ForegroundColor DarkYellow Write-Host "" # Step 1: Verify Graph connection Write-Host "[1/5] Verifying Microsoft Graph connection..." -ForegroundColor Cyan try { Test-GraphConnection Write-Host " Graph connection verified." -ForegroundColor Green } catch { Write-Error "Graph connection failed: $_" Write-Host "" Write-Host "To connect, run:" -ForegroundColor Yellow Write-Host " Connect-MgGraph -Scopes 'User.Read.All','Directory.Read.All','Reports.Read.All'" -ForegroundColor White return } # Step 2: License Inventory Write-Host "[2/5] Collecting license inventory..." -ForegroundColor Cyan try { $inventory = Get-LicenseInventory Write-Host " Found $($inventory.Count) SKU(s)." -ForegroundColor Green } catch { Write-Error "Failed to collect license inventory: $_" return } # Step 3: Underutilized Licenses Write-Host "[3/5] Analyzing underutilized licenses..." -ForegroundColor Cyan try { $underutilized = Get-UnderutilizedLicenses -DaysInactive $DaysInactive $underCount = if ($underutilized) { @($underutilized).Count } else { 0 } Write-Host " Found $underCount underutilized license(s)." -ForegroundColor Green } catch { Write-Warning "Underutilized license analysis failed: $_" $underutilized = @() } # Step 4: Inactive Licensed Users Write-Host "[4/5] Identifying inactive licensed users..." -ForegroundColor Cyan try { $inactiveParams = @{ DaysInactive = $DaysInactive } if ($IncludeGuests) { $inactiveParams['IncludeGuests'] = $true } $inactiveUsers = Get-InactiveLicensedUsers @inactiveParams $inactiveCount = if ($inactiveUsers) { @($inactiveUsers).Count } else { 0 } Write-Host " Found $inactiveCount inactive licensed user(s)." -ForegroundColor Green } catch { Write-Warning "Inactive user analysis failed: $_" $inactiveUsers = @() } # Step 5: Savings Report Write-Host "[5/5] Calculating savings..." -ForegroundColor Cyan try { $savingsReport = Get-LicenseSavingsReport -DaysInactive $DaysInactive } catch { Write-Warning "Savings report generation failed: $_" $savingsReport = @() } # Generate HTML dashboard $timestamp = Get-Date -Format 'yyyyMMdd-HHmmss' $reportFile = Join-Path -Path $OutputPath -ChildPath "M365-LicenseOptimization-$timestamp.html" Write-Host "Generating HTML dashboard..." -ForegroundColor Cyan try { $htmlFile = New-HtmlDashboard ` -Title "M365 License Optimization Report" ` -LicenseInventory $inventory ` -UnderutilizedLicenses $underutilized ` -InactiveUsers $inactiveUsers ` -SavingsReport $savingsReport ` -OutputPath $reportFile Write-Host "" Write-Host "Report saved to: $($htmlFile.FullName)" -ForegroundColor Green } catch { Write-Warning "HTML dashboard generation failed: $_" } $stopwatch.Stop() Write-Host "Completed in $([math]::Round($stopwatch.Elapsed.TotalSeconds, 1)) seconds." -ForegroundColor DarkGray Write-Host "" return $htmlFile } |