Scripts/Get-ProductLicenses.ps1
Function Get-Licenses { <# .SYNOPSIS Retrieves all licenses in the tenant with retention times and premium license indicators. .DESCRIPTION Returns all available licenses in the tenant along with their retention times and premium license indicators, and exports the details to a CSV file. .PARAMETER OutputDir OutputDir is the parameter specifying the output directory. Default: Output\Licenses .PARAMETER LogLevel Specifies the level of logging: None: No logging Minimal: Critical errors only Standard: Normal operational logging Default: Standard .EXAMPLE Get-Licenses Retrieves all licenses and saves them to a CSV file in Output\Licenses. #> [CmdletBinding()] param( [string]$OutputDir = "Output\Licenses", [ValidateSet('None', 'Minimal', 'Standard')] [string]$LogLevel = 'Standard' ) Set-LogLevel -Level ([LogLevel]::$LogLevel) if (!(Test-Path $OutputDir)) { New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null } Write-LogFile -Message "=== Starting License Collection ===" -Color "Cyan" -Level Minimal try { $licenses = Get-MgSubscribedSku | Select-Object SkuPartNumber, CapabilityStatus, AppliesTo, ConsumedUnits, ServicePlans if (-not $licenses) { Write-LogFile -Message "[ERROR] No licenses found in the tenant." -Color "Red" -Level Minimal return } $results = $licenses | ForEach-Object { $servicePlanNames = $_.ServicePlans.ServicePlanName -join '; ' $servicePlansForChecks = $_.ServicePlans.ServicePlanName [PSCustomObject]@{ Sku = $_.SkuPartNumber Status = $_.CapabilityStatus Scope = $_.AppliesTo Units = $_.ConsumedUnits Retention = if ($_.SkuPartNumber -match "E5") { "365 days" } elseif ($_.SkuPartNumber -match "E3") { "180 days" } else { "90 days" } E3 = if ($_.SkuPartNumber -in @("M365ENTERPRISE", "ENTERPRISEPACK", "STANDARD_EDU")) { "Yes" } else { "No" } E5 = if ($_.SkuPartNumber -in @("SPE_E5", "ENTERPRISEPREMIUM")) { "Yes" } else { "No" } P1 = if ($servicePlansForChecks -contains "AAD_PREMIUM") { "Yes" } else { "No" } P2 = if ($servicePlansForChecks -contains "AAD_PREMIUM_P2") { "Yes" } else { "No" } DefenderID = if ($servicePlansForChecks -contains "MDE_ATP") { "Yes" } else { "No" } Defender365P1 = if ($servicePlansForChecks -contains "ATP_ENTERPRISE") { "Yes" } else { "No" } Defender365P2 = if ($servicePlansForChecks -contains "ATP_ENTERPRISE_PLUS") { "Yes" } else { "No" } ServicePlans = $servicePlanNames } } $date = [datetime]::Now.ToString('yyyyMMddHHmmss') $outputFile = Join-Path $OutputDir "$($date)-TenantLicenses.csv" $results | Sort-Object -Property Units -Descending | Export-Csv -Path $outputFile -NoTypeInformation -Encoding UTF8 Write-LogFile -Message "[INFO] License information saved to: $outputFile" -Color "Green" -Level Standard Write-LogFile -Message "`nLicense Information:" -Color "Cyan" -Level Standard return $results | Sort-Object -Property Units -Descending | Format-Table -Property @( @{Label = "License Name"; Expression = {$_.Sku}; Width = 30}, @{Label = "Status"; Expression = {$_.Status}; Width = 10}, @{Label = "Units"; Expression = {$_.Units}; Width = 8; Alignment = "Right"}, @{Label = "Retention"; Expression = {$_.Retention}; Width = 12}, @{Label = "E3"; Expression = {$_.E3}; Width = 5}, @{Label = "E5"; Expression = {$_.E5}; Width = 5}, @{Label = "P1"; Expression = {$_.P1}; Width = 5}, @{Label = "P2"; Expression = {$_.P2}; Width = 5}, @{Label = "DefenderID"; Expression = {$_.DefenderID}; Width = 12}, @{Label = "Def365P1"; Expression = {$_.Defender365P1}; Width = 10}, @{Label = "Def365P2"; Expression = {$_.Defender365P2}; Width = 10} ) -AutoSize } catch { Write-LogFile -Message "[ERROR] Failed to retrieve licenses: $($_.Exception.Message)" -Color "Red" -Level Minimal throw } } Function Get-LicenseCompatibility { <# .SYNOPSIS Checks the presence of E5, P2, P1, and E3 licenses and informs about functionality limitations. .DESCRIPTION Determines if E5, P2, P1, and E3 licenses are present and outputs messages regarding the capabilities and limitations. .PARAMETER LogLevel Specifies the level of logging: None: No logging Minimal: Critical errors only Standard: Normal operational logging Default: Standard .EXAMPLE Get-LicenseCompatibility Checks for E5, P2, P1, and E3 licenses and outputs corresponding limitations. #> [CmdletBinding()] param( [ValidateSet('None', 'Minimal', 'Standard')] [string]$LogLevel = 'Standard' ) Set-LogLevel -Level ([LogLevel]::$LogLevel) Write-LogFile -Message "=== Starting License Compatibility Check ===" -Color "Cyan" -Level Minimal try { $licenses = Get-MgSubscribedSku $allServicePlans = $licenses | ForEach-Object { $_.ServicePlans.ServicePlanName } $global:e5Present = $licenses | Where-Object { $_.SkuPartNumber -match "E5" } $global:e3Present = $licenses | Where-Object { $_.SkuPartNumber -match "E3" } $global:p1Present = $allServicePlans -contains "AAD_PREMIUM" $global:p2Present = $allServicePlans -contains "AAD_PREMIUM_P2" Write-LogFile -Message "`nLicense Status:" -Color "Cyan" -Level Standard Write-LogFile -Message "E5: $(if($global:e5Present){"Present"}else{"Not Present"})" -Color $(if($global:e5Present){"Green"}else{"Yellow"}) -Level Standard if (-not $global:e5Present) { Write-LogFile -Message "E3: $(if($global:e3Present){"Present"}else{"Not Present"})" -Color $(if($global:e3Present){"Green"}else{"Yellow"}) -Level Standard } Write-LogFile -Message "P2: $(if($global:p2Present){"Present"}else{"Not Present"})" -Color $(if($global:p2Present){"Green"}else{"Yellow"}) -Level Standard if (-not ($global:p2Present -or $global:e5Present)) { Write-LogFile -Message "P1: $(if($global:p1Present){"Present"}else{"Not Present"})" -Color $(if($global:p1Present){"Green"}else{"Yellow"}) -Level Standard } Write-LogFile -Message "`nFeature Compatibility:" -Color "Cyan" -Level Standard $features = @( @{Feature = "Get-Sessions"; Required = "E5"; Status = $global:e5Present} @{Feature = "Get-MessageIDs"; Required = "E5"; Status = $global:e5Present} @{Feature = "Get-GraphEntraAuditLogs"; Required = "E5"; Status = $global:e5Present} @{Feature = "Get-RiskyUsers"; Required = "E5 or P2"; Status = ($global:e5Present -or $global:p2Present)} ) foreach ($feature in $features) { $status = if ($feature.Status) { "Available" } else { "Not Available" } $color = if ($feature.Status) { "Green" } else { "Yellow" } Write-LogFile -Message "$($feature.Feature) ($($feature.Required)): $status" -Color $color -Level Standard } Write-LogFile -Message "`nRetention Information:" -Color "Cyan" -Level Standard if ($global:e3Present -or $global:e5Present -or $global:p1Present -or $global:p2Present) { Write-LogFile -Message "Audit Log retention: 30 days" -Color "Green" -Level Standard Write-LogFile -Message "Sign-in Log retention: 30 days" -Color "Green" -Level Standard } else { Write-LogFile -Message "Audit Log retention: 7 days" -Color "Yellow" -Level Standard Write-LogFile -Message "Sign-in Log retention: 7 days" -Color "Yellow" -Level Standard } $recommendations = @() if (-not $global:e5Present) { $recommendations += "- Consider E5 license for full feature access and extended retention" } if (-not $global:p2Present -and -not $global:e5Present) { $recommendations += "- P2 license would enable risky users monitoring" } if (-not ($global:e3Present -or $global:e5Present -or $global:p1Present -or $global:p2Present)) { $recommendations += "- Current retention period is limited. Consider upgrading for extended retention" } if ($recommendations.Count -gt 0) { Write-LogFile -Message "`nRecommendations:" -Color "Cyan" -Level Standard foreach ($recommendation in $recommendations) { Write-LogFile -Message $recommendation -Color "Yellow" -Level Standard } } } catch { Write-LogFile -Message "[ERROR] Failed to check license capabilities: $($_.Exception.Message)" -Color "Red" -Level Minima throw } } Function Get-EntraSecurityDefaults { <# .SYNOPSIS Checks the status of Entra ID security defaults. .DESCRIPTION Retrieves and logs the status of security defaults and exports the result to a CSV file. .PARAMETER OutputDir OutputDir is the parameter specifying the output directory. Default: Output\Licenses .PARAMETER LogLevel Specifies the level of logging: None: No logging Minimal: Critical errors only Standard: Normal operational logging Default: Standard .EXAMPLE Get-EntraSecurityDefaults Checks the status of security defaults and saves the results to a CSV file in Output\Licenses. #> [CmdletBinding()] param( [string]$OutputDir = "Output\Licenses", [ValidateSet('None', 'Minimal', 'Standard')] [string]$LogLevel = 'Standard' ) Set-LogLevel -Level ([LogLevel]::$LogLevel) if (!(Test-Path $OutputDir)) { New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null } Write-LogFile -Message "=== Starting Security Defaults Check ===" -Color "Cyan" -Level Minimal try { if ($null -eq $global:e5Present) { $licenses = Get-MgSubscribedSku $allServicePlans = $licenses | ForEach-Object { $_.ServicePlans.ServicePlanName } $global:e5Present = $licenses | Where-Object { $_.SkuPartNumber -match "E5" } $global:e3Present = $licenses | Where-Object { $_.SkuPartNumber -match "E3" } $global:p2Present = $allServicePlans -contains "AAD_PREMIUM_P2" $global:p1Present = $allServicePlans -contains "AAD_PREMIUM" } $securityDefaults = Get-MgPolicyIdentitySecurityDefaultEnforcementPolicy $isEnabled = $securityDefaults.IsEnabled $hasPremiumLicense = $global:e5Present -or $global:e3Present -or $global:p2Present -or $global:p1Present Write-LogFile -Message "`nSecurity Defaults Status:" -Color "Cyan" -Level Standard if ($isEnabled) { Write-LogFile -Message "Security Defaults: Enabled" -Color "Green" -Level Standard } else { Write-LogFile -Message "Security Defaults: Disabled" -Color "Yellow" -Level Standard } $securityDefaults = Get-MgPolicyIdentitySecurityDefaultEnforcementPolicy $isEnabled = $securityDefaults.IsEnabled $result = [PSCustomObject]@{ SecurityDefaultsEnabled = if ($isEnabled) { "Yes" } else { "No" } } Write-LogFile -Message "`nLicense Context:" -Color "Cyan" -Level Standard if ($hasPremiumLicense) { Write-LogFile -Message "Premium License(s) Detected:" -Level Standard if ($global:e5Present) { Write-LogFile -Message " - E5" -Level Standard } if ($global:e3Present -and -not $global:e5Present) { Write-LogFile -Message " - E3" -Level Standard } if ($global:p2Present -and -not $global:e5Present) { Write-LogFile -Message " - P2" -Level Standard } if ($global:p1Present -and -not ($global:p2Present -or $global:e5Present)) { Write-LogFile -Message " - P1" -Level Standard } } else { Write-LogFile -Message "No Premium Licenses Detected" -Level Standard } Write-LogFile -Message "`nRecommendations:" -Color "Cyan" -Level Standard if ($hasPremiumLicense) { if ($isEnabled) { Write-LogFile -Message "[!] With your current license level (Premium), Microsoft recommends:" -Color "Yellow" -Level Standard Write-LogFile -Message " - Disable Security Defaults" -Level Standard Write-LogFile -Message " - Configure Conditional Access policies for greater control" -Level Standard Write-LogFile -Message " - Implement MFA through Conditional Access" -Level Standard } else { Write-LogFile -Message "Current configuration aligns with Microsoft recommendations" -Color "Green" -Level Standard Write-LogFile -Message " - Ensure Conditional Access policies are properly configured" -Level Standard Write-LogFile -Message " - Regular review of security policies is recommended" -Level Standard } } else { if ($isEnabled) { Write-LogFile -Message "Current configuration aligns with Microsoft recommendations" -Color "Green" -Level Standard Write-LogFile -Message " - Security Defaults provide basic security for free/basic licenses" -Level Standard } else { Write-LogFile -Message "[!] With your current license level (Basic), Microsoft recommends:" -Color "Red" -Level Minimal Write-LogFile -Message " - Enable Security Defaults for baseline protection" -Level Standard Write-LogFile -Message " - Consider upgrading to premium license for advanced security features" -Level Standard } } $result = [PSCustomObject]@{ SecurityDefaultsEnabled = if ($isEnabled) { "Yes" } else { "No" } HasPremiumLicense = if ($hasPremiumLicense) { "Yes" } else { "No" } RecommendedState = if ($hasPremiumLicense) { "Disabled" } else { "Enabled" } AlignedWithRecommendations = if (($hasPremiumLicense -and -not $isEnabled) -or (-not $hasPremiumLicense -and $isEnabled)) { "Yes" } else { "No" } CheckDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss" } $date = [datetime]::Now.ToString('yyyyMMddHHmmss') $outputFile = Join-Path $OutputDir "$($date)-EntraSecurityDefaults.csv" $result | Export-Csv -Path $outputFile -NoTypeInformation -Encoding UTF8 Write-LogFile -Message "`nCheck Summary:" -Color "Cyan" -Level Standard Write-LogFile -Message ($result | Format-List | Out-String).Trim() -Level Standard Write-LogFile -Message "`nOutput Files:" -Color "Cyan" -Level Standard Write-LogFile -Message "- Results exported to: $outputFile" -Color "Green" -Level Standard } catch { Write-LogFile -Message "[ERROR] Failed to check security defaults: $($_.Exception.Message)" -Color "Red" -Level Minimal throw } } Function Get-LicensesByUser { <# .SYNOPSIS Retrieves license assignments for all users in the tenant. .DESCRIPTION Retrieves all licenses assigned to users in the tenant and saves the results to a CSV file. .PARAMETER OutputDir OutputDir is the parameter specifying the output directory. Default: Output\Licenses .PARAMETER LogLevel Specifies the level of logging: None: No logging Minimal: Critical errors only Standard: Normal operational logging Default: Standard .EXAMPLE Get-LicensesByUser -OutputDir "Output\Licenses" Retrieves license assignments and saves the details to a CSV file in the specified directory. #> [CmdletBinding()] param( [string]$OutputDir = "Output\Licenses", [ValidateSet('None', 'Minimal', 'Standard')] [string]$LogLevel = 'Standard' ) Set-LogLevel -Level ([LogLevel]::$LogLevel) if (!(Test-Path $OutputDir)) { New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null } Write-LogFile -Message "=== Starting User License Collection ===" -Color "Cyan" -Level Minimal try { if (!(Get-MgContext)) { Connect-MgGraph -Scopes "User.Read.All", "Directory.Read.All" } $skus = Get-MgSubscribedSku | Select-Object SkuId, SkuPartNumber $results = @() $users = Get-MgUser -All -Property DisplayName, UserPrincipalName, Id if (-not $users) { Write-LogFile -Message "[ERROR] No users retrieved. Ensure you have sufficient permissions and users exist in the tenant." -Color "Red" -Level Minimal return } foreach ($user in $users) { if (-not $user.Id) { Write-LogFile -Message "[ALERT] Skipping user: $($user.DisplayName) - Missing 'Id' property" -Color "Yellow" -Level Standard continue } try { $licenseDetails = Get-MgUserLicenseDetail -UserId $user.Id if ($licenseDetails) { foreach ($license in $licenseDetails) { $skuPartNumber = ($skus | Where-Object { $_.SkuId -eq $license.SkuId }).SkuPartNumber $results += [PSCustomObject]@{ DisplayName = $user.DisplayName UserPrincipalName = $user.UserPrincipalName SkuPartNumber = $skuPartNumber } } } else { $results += [PSCustomObject]@{ DisplayName = $user.DisplayName UserPrincipalName = $user.UserPrincipalName SkuPartNumber = "None" } } } catch { Write-LogFile -Message "[ERROR] Failed to retrieve license details for user $($user.UserPrincipalName): $($_.Exception.Message)" -Color "Red" -Level Minimal } } $date = [datetime]::Now.ToString('yyyyMMddHHmmss') $outputFile = Join-Path $OutputDir "$($date)-UserLicenses.csv" $results | Sort-Object DisplayName | Export-Csv -Path $outputFile -NoTypeInformation -Encoding UTF8 $userLicenseSummary = [PSCustomObject]@{ TotalUsers = ($users | Measure-Object).Count LicensedUsers = ($results | Select-Object -Unique UserPrincipalName | Measure-Object).Count UnlicensedUsers = (($results | Where-Object { $_.SkuPartNumber -eq "None" }) | Measure-Object).Count TotalAssignments = ($results | Where-Object { $_.SkuPartNumber -ne "None" } | Measure-Object).Count } $licenseDistribution = $results | Where-Object { $_.SkuPartNumber -ne "None" } | Group-Object SkuPartNumber | Sort-Object Count -Descending Write-LogFile -Message "`nUser License Summary:" -Color "Cyan" -Level Standard Write-LogFile -Message "Total Users: $($userLicenseSummary.TotalUsers)" -Level Standard Write-LogFile -Message " - Total License Assignments: $($userLicenseSummary.TotalAssignments)" -Level Standard Write-LogFile -Message " - Licensed Users: $($userLicenseSummary.LicensedUsers)" -Level Standard Write-LogFile -Message " - Unlicensed Users: $($userLicenseSummary.UnlicensedUsers)" -Level Standard Write-LogFile -Message "`nLicense Type Distribution:" -Color "Cyan" -Level Standard foreach ($license in $licenseDistribution) { Write-LogFile -Message " - $($license.Name): $($license.Count) assignments" -Level Standard } Write-LogFile -Message "`nExported File:" -Color "Cyan" -Level Standard Write-LogFile -Message " - File: $outputFile" -Level Standard } catch { Write-LogFile -Message "[ERROR] Failed to retrieve user license assignments: $($_.Exception.Message)" -Color "Red" -Level Minimal throw } } |