get-O365LicenseOverview.ps1
<#PSScriptInfo
.VERSION 1.0 .GUID ad23a25e-d924-42a1-b9bf-30e5ef65c170 .AUTHOR Maarten Peeters - Cloud Securitea - https://www.cloudsecuritea.com .COMPANYNAME Cloud Securitea .COPYRIGHT .TAGS Subscriptions, licenses, office365 .LICENSEURI .PROJECTURI .ICONURI .EXTERNALMODULEDEPENDENCIES MSOnline .RELEASENOTES Version 1.0: Original published version. #> <# .SYNOPSIS Quickly generate an Office 365 subscription and license overview .DESCRIPTION This script will generate a HTML file to list the following: List of all subscriptions List of all SKU's combined List of all groups who have licenses connected to them List of all unlicensed users who have no role assigned List of all unlicensed users who have a role assigned List of all licensed users who have a role assigned List of all licensed users who get their license directly List of all licensed users who get their license via group assignement .PARAMETER LogPath Enter the full path to store the HTML report of the Office 365 subscription and license overview For example: C:\Install .EXAMPLE Get-O365LicenseOverview.ps1 -LogPath "C:\Install" .NOTES Version: 1.0 Author: Maarten Peeters Creation Date: 04-09-2019 Purpose/Change: Quickly generate an overview of Office 365 Subscriptions and licenses #> param( [Parameter(mandatory=$true)] [string] $LogPath ) try{ #Verify if MSOnline Module is available if (Get-Module -ListAvailable -Name MSonline) { #Import MSOnline Module import-module MSOnline -ErrorAction SilentlyContinue #Test if logpath exists If(Test-Path $LogPath) { #Start script Try{ #Object collections $subscriptionCollection = @() $accountSkuCollection = @() $licensedGroupCollection = @() $usersWithRolesCollection = @() $usersLicensedByGroupCollection = @() $usersLicensedDirectlyCollection = @() $usersUnlicensedNoRoleCollection = @() $usersUnlicensedWithRoleCollection = @() $usersLicensedWithRoleCollection = @() #Connect to the correct O365 Tenant Connect-MsolService #Retrieve all subscriptions and place this information in an object $subscriptions = Get-MsolSubscription foreach($subscription in $subscriptions){ $subscriptionCollection += new-object psobject -property @{ObjectId=$subscription.ObjectId;SkuPartNumber=$subscription.SkuPartNumber;Status=$subscription.Status;TotalLicenses=$subscription.TotalLicenses} } #Retrieve all account Sku's and place this information in an object $accountSkus = Get-MsolAccountSku foreach($accountSku in $accountSkus){ $accountSkuCollection += new-object psobject -property @{SkuPartNumber=$accountSku.SkuPartNumber;ActiveUnits=$accountSku.ActiveUnits;ConsumedUnits=$accountSku.ConsumedUnits} } #Retrieve group which have licenses assigned to them and place this information in an object $licensedGroups = Get-MsolGroup | Where-Object {$_.licenses -ne ""} foreach($licensedGroup in $licensedGroups){ $groupMembers = (Get-MsolGroupMember -GroupObjectId $licensedGroup.ObjectId).count $licenses = $licensedGroup.licenses foreach($license in $licenses){ $licensedGroupCollection += new-object psobject -property @{ObjectID=$licensedGroup.objectID;DisplayName=$licensedGroup.DisplayName;Members=$GroupMembers;license=$license.SkuPartNumber} } } #Retrieve all users who have roles assigned $roles = Get-MsolRole foreach($role in $roles){ $members = Get-MsolRoleMember -RoleObjectId $role.ObjectId foreach($member in $members){ $usersWithRolesCollection += new-object psobject -property @{DisplayName=$member.DisplayName;RoleName=$role.name} } } #Retrieve all users and place them in their appropiate collection $users = Get-MsolUser | Sort-Object DisplayName foreach ($user in $users){ #check if synced account if($user.ImmutableId -eq $null){ $synced = "False" } else{ $synced = "False" } #Unlicensed users if ($user.isLicensed -eq $false){ #if the user has an admin role or not $adminRole = "no" foreach($userWithRoles in $usersWithRolesCollection){ if($user.DisplayName -eq $userWithRoles.DisplayName){ $adminRole = "yes" } } if($adminRole -eq "no"){ $usersUnlicensedNoRoleCollection += new-object psobject -property @{DisplayName=$user.DisplayName;UserType=$user.UserType;UPN=$user.userprincipalname;License=$user.isLicensed;Admin="False";BlockCredential=$user.BlockCredential;Synced=$Synced} } else{ $usersUnlicensedWithRoleCollection += new-object psobject -property @{DisplayName=$user.DisplayName;UserType=$user.UserType;UPN=$user.userprincipalname;License=$user.isLicensed;Admin="True";BlockCredential=$user.BlockCredential;Synced=$Synced} } } #Licensed with admin role if ($user.isLicensed -eq $true){ #if the user has an admin role or not $adminRole = "no" foreach($userWithRoles in $usersWithRolesCollection){ if($user.DisplayName -eq $userWithRoles.DisplayName){ $adminRole = "yes" } } if($adminRole -eq "yes"){ $usersLicensedWithRoleCollection += new-object psobject -property @{DisplayName=$user.DisplayName;UserType=$user.UserType;UPN=$user.userprincipalname;License=$user.isLicensed;Admin="True";BlockCredential=$user.BlockCredential;Synced=$Synced} } } #Licensed users and verify if the license is coming from a group or directly if ($user.isLicensed -eq $true){ $licenses = $user.licenses foreach($license in $licenses){ #You can use Groups Assigning License but I noticed that if it's not assigned via a group it will be the users GUID. I will verify the group with the earlier collection. $groupAssignedFound = "no" $groupAssigned = "" foreach($groupAssigningLicense in $license.GroupsAssigningLicense){ $groupAssigned = $licensedGroupCollection | where-object {$_.ObjectID -eq $groupAssigningLicense.GUID} if($groupAssigned){ $groupAssignedFound = "yes" } } if ($groupAssignedFound -eq "no"){ $usersLicensedDirectlyCollection += new-object psobject -property @{DisplayName=$user.DisplayName;UserType=$user.UserType;UPN=$user.userprincipalname;License=$license.accountsku.skupartnumber;BlockCredential=$user.BlockCredential;Synced=$Synced} } else{ $usersLicensedByGroupCollection += new-object psobject -property @{DisplayName=$user.DisplayName;UserType=$user.UserType;UPN=$user.userprincipalname;License=$license.accountsku.skupartnumber;BlockCredential=$user.BlockCredential;Synced=$Synced} } } } } #We now have our collections so we are building the HTML page to get a direct view #List of all subscriptions $article = "<h2>List of all subscriptions</h2>" $article += "<table> <tr> <th>ObjectId</th> <th>SkuPartNumber</th> <th>Status</th> <th>TotalLicenses</th> </tr>" foreach($subscription in $subscriptionCollection){ $article += "<tr> <td>$($subscription.ObjectId)</td> <td>$($subscription.SkuPartNumber)</td> <td>$($subscription.Status)</td> <td>$($subscription.TotalLicenses)</td> </tr>" } $article += "</table>" #List of all SKU's combined $article += "<h2>List of all SKU's combined</h2>" $article += "<table> <tr> <th>SkuPartNumber</th> <th>ActiveUnits</th> <th>ConsumedUnits</th> </tr>" foreach($accountSku in $accountSkuCollection){ $article += "<tr> <td>$($accountSku.SkuPartNumber)</td> <td>$($accountSku.ActiveUnits)</td> <td>$($accountSku.ConsumedUnits)</td> </tr>" } $article += "</table>" #List of all groups who have licenses connected to them $article += "<h2>List of all groups who have licenses connected to them</h2>" $article += "<table> <tr> <th>ObjectID</th> <th>DisplayName</th> <th>Members</th> <th>license</th> </tr>" foreach($licensedGroup in $licensedGroupCollection){ $article += "<tr> <td>$($licensedGroup.ObjectID)</td> <td>$($licensedGroup.DisplayName)</td> <td>$($licensedGroup.Members)</td> <td>$($licensedGroup.license)</td> </tr>" } $article += "</table>" #List of all unlicensed users who have no role assigned $article += "<h2>List of all unlicensed users who have no role assigned</h2>" $article += "<table> <tr> <th>DisplayName</th> <th>UserType</th> <th>UPN</th> <th>license</th> <th>Admin</th> <th>BlockCredential</th> <th>Synced</th> </tr>" foreach($usersUnlicensedNoRole in $usersUnlicensedNoRoleCollection){ $article += "<tr> <td>$($usersUnlicensedNoRole.DisplayName)</td> <td>$($usersUnlicensedNoRole.UserType)</td> <td>$($usersUnlicensedNoRole.UPN)</td> <td>$($usersUnlicensedNoRole.license)</td> <td>$($usersUnlicensedNoRole.Admin)</td> <td>$($usersUnlicensedNoRole.BlockCredential)</td> <td>$($usersUnlicensedNoRole.Synced)</td> </tr>" } $article += "</table>" #List of all unlicensed users who have a role assigned $article += "<h2>List of all unlicensed users who have a role assigned</h2>" $article += "<table> <tr> <th>DisplayName</th> <th>UserType</th> <th>UPN</th> <th>license</th> <th>Admin</th> <th>BlockCredential</th> <th>Synced</th> </tr>" foreach($usersUnlicensedWithRole in $usersUnlicensedWithRoleCollection){ $article += "<tr> <td>$($usersUnlicensedWithRole.DisplayName)</td> <td>$($usersUnlicensedWithRole.UserType)</td> <td>$($usersUnlicensedWithRole.UPN)</td> <td>$($usersUnlicensedWithRole.license)</td> <td>$($usersUnlicensedWithRole.Admin)</td> <td>$($usersUnlicensedWithRole.BlockCredential)</td> <td>$($usersUnlicensedWithRole.Synced)</td> </tr>" } $article += "</table>" #List of all licensed users who have a role assigned $article += "<h2>List of all licensed users who have a role assigned</h2>" $article += "<table> <tr> <th>DisplayName</th> <th>UserType</th> <th>UPN</th> <th>license</th> <th>Admin</th> <th>BlockCredential</th> <th>Synced</th> </tr>" foreach($usersLicensedWithRole in $usersLicensedWithRoleCollection){ $article += "<tr> <td>$($usersLicensedWithRole.DisplayName)</td> <td>$($usersLicensedWithRole.UserType)</td> <td>$($usersLicensedWithRole.UPN)</td> <td>$($usersLicensedWithRole.license)</td> <td>$($usersLicensedWithRole.Admin)</td> <td>$($usersLicensedWithRole.BlockCredential)</td> <td>$($usersLicensedWithRole.Synced)</td> </tr>" } $article += "</table>" #List of all licensed users who get their license directly $article += "<h2>List of all licensed users who get their license directly</h2>" $article += "<table> <tr> <th>DisplayName</th> <th>UserType</th> <th>UPN</th> <th>license</th> <th>BlockCredential</th> <th>Synced</th> </tr>" foreach($usersLicensedDirectly in $usersLicensedDirectlyCollection){ $article += "<tr> <td>$($usersLicensedDirectly.DisplayName)</td> <td>$($usersLicensedDirectly.UserType)</td> <td>$($usersLicensedDirectly.UPN)</td> <td>$($usersLicensedDirectly.license)</td> <td>$($usersLicensedDirectly.BlockCredential)</td> <td>$($usersLicensedDirectly.Synced)</td> </tr>" } $article += "</table>" #List of all licensed users who get their license via group assignement $article += "<h2>List of all licensed users who get their license via group assignement</h2>" $article += "<table> <tr> <th>DisplayName</th> <th>UserType</th> <th>UPN</th> <th>license</th> <th>BlockCredential</th> <th>Synced</th> </tr>" foreach($usersLicensedByGroup in $usersLicensedByGroupCollection){ $article += "<tr> <td>$($usersLicensedByGroup.DisplayName)</td> <td>$($usersLicensedByGroup.UserType)</td> <td>$($usersLicensedByGroup.UPN)</td> <td>$($usersLicensedByGroup.license)</td> <td>$($usersLicensedByGroup.BlockCredential)</td> <td>$($usersLicensedByGroup.Synced)</td> </tr>" } $article += "</table>" $date = get-date $today = $date.ToString("ddMMyyyy_HHmm") $LogPath = Join-Path $LogPath "HTMLSubscriptionReport_$($today).html" #Head $head = " <html xmlns=`"http://www.w3.org/1999/xhtml`"> <head> <style> @charset `"UTF-8`"; @media print { body {-webkit-print-color-adjust: exact;} } div.container { width: 100%; border: 1px solid gray; } header { padding: 0.1em; color: white; background-color: #000033; color: white; clear: left; text-align: center; border-bottom: 2px solid #FF0066 } footer { padding: 0.1em; color: white; background-color: #000033; color: white; clear: left; text-align: center; border-top: 2px solid #FF0066 } article { margin-left: 20px; min-width:600px; min-height: 600px; padding: 1em; } th{ border:1px Solid Black; border-Collapse:collapse; background-color:#000033; color:white; } th{ border:1px Solid Black; border-Collapse:collapse; } </style> </head> " #Header $date = (get-date).tostring("dd-MM-yyyy") $header = " <h1>Subscriptions Report</h1> <h5>$($date)</h5> " #Footer $Footer = " Copyright © " #Full HTML $HTML = " $($Head) <body class=`"Inventory`"> <div class=`"container`"> <header> $($Header) </header> <article> $($article) </article> <footer> $($footer) </footer> </div> </body> </html> " add-content $HTML -path $LogPath Write-Host "Office 365 subscription and license overview created at $($LogPath), it will also open automatically in 5 seconds" -foregroundcolor green start-sleep -s 5 Invoke-Item $LogPath } catch{ write-host "Error occurred: $($_.Exception.Message), please post this error on https://www.cloudsecuritea.com" -foregroundcolor red } } Else { Write-Host "The path $($LogPath) could not be found. Please enter a correct path to store the Office 365 subscription and license overview" -foregroundcolor yellow } } else { Write-Host "MSOnline module not loaded. Please install the MSOnline module with Install-Module MSOnline" -foregroundcolor yellow } } catch{ write-host "Error occurred: $($_.Exception.Message)" -foregroundcolor red } |