source/main/Get-SkuMonData.ps1

Function Get-SkuMonData {
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline)]
        $SkuMonList,

        [parameter()]
        [ValidateSet('PSObject', 'Html', 'Email')]
        [string]
        $OutputType = 'PSObject',

        [parameter()]
        [string]$From,

        [parameter()]
        [string[]]$To,

        [parameter()]
        [string[]]$CC,

        [parameter()]
        [string[]]$BCC
    )
    begin {
        # If OutputType is Email, set the requirements.
        if ($OutputType -eq 'Email') {
            if (!$From) {
                Say 'The From email address is required.'
                return $null
            }

            if (!$To -and !$Cc -and !$Bcc) {
                Say 'There must be at least 1 recipient email address.'
                return $null
            }
        }

        # JSON email address conversion
        Function ConvertRecipientsToJSON {
            param(
                [Parameter(Mandatory)]
                [string[]]
                $Recipients
            )
            $jsonRecipients = @()
            $Recipients | ForEach-Object {
                $jsonRecipients += @{EmailAddress = @{Address = $_ } }
            }
            return $jsonRecipients
        }

        $ThisFunction = ($MyInvocation.MyCommand)
        $ThisModule = Get-Module ($ThisFunction.Source)

        if (!$SkuMonList) {
            $SkuMonList = New-SkuMonList
        }

        $subscribedSku = Get-MgSubscribedSku -ErrorAction Stop | Where-Object { $_.AppliesTo -eq 'User' }
        [System.Collections.ArrayList]$skuCollection = @()
    }

    process {
        foreach ($item in $SkuMonList | Where-Object { $_.IncludeInReport -eq $true }) {
            $sku = $subscribedSku | Where-Object { $_.SkuPartNumber -eq $item.SkuPartNumber }

            $AvailableUnits = (($sku.prepaidUnits.Enabled + $sku.prepaidUnits.Warning) - $sku.ConsumedUnits)
            $ExcessUnits = 0
            if ($AvailableUnits -lt 0) {
                $ExcessUnits = [Math]::Abs($AvailableUnits)
                $AvailableUnits = 0
            }

            $null = $skuCollection.Add(
                $(
                    New-Object psobject -Property (
                        [ordered]@{
                            PSTypeName       = 'SkuMonData'
                            SkuID            = $sku.SkuID
                            SkuPartNumber    = $sku.SkuPartNumber
                            SkuName          = $(
                                if (!($item.SkuName)) {
                                    $item.SkuPartNumber
                                }
                                else {
                                    $item.SkuName
                                }
                            )
                            Assigned         = $sku.ConsumedUnits
                            Total            = $sku.prepaidUnits.Enabled
                            Suspended        = $sku.prepaidUnits.Suspended
                            Warning          = $sku.prepaidUnits.Warning
                            Available        = $AvailableUnits
                            Invalid          = $ExcessUnits
                            CapabilityStatus = $sku.CapabilityStatus
                            AlertThreshold   = $item.AlertThreshold
                            ThresholdStatus  = $(
                                if ($item.AlertThreshold -gt 0) {
                                    if ($AvailableUnits -le $item.AlertThreshold) {
                                        "Warning"
                                    }

                                    if ($AvailableUnits -gt $item.AlertThreshold) {
                                        "Normal"
                                    }
                                }
                                else {
                                    "Ignore"
                                }
                            )
                        }
                    )
                )
            )
        }
    }

    end {

        if ($OutputType -eq 'Html' -or $OutputType -eq 'Email') {
            $Organization = Get-MgOrganization
            $ResourceFolder = [System.IO.Path]::Combine((Split-Path ($ThisModule.Path) -Parent), 'resource')
            $css = Get-Content $resourceFolder\style.css -Raw

            if ($PSVersionTable.PSEdition -eq 'Core') {
                $logo = $([convert]::ToBase64String((Get-Content $resourceFolder\logo.png -AsByteStream)))
            }
            else {
                $logo = $([convert]::ToBase64String((Get-Content $resourceFolder\logo.png -Raw -Encoding byte)))
            }

            $timeZoneInfo = [System.TimeZoneInfo]::Local
            $tz = $timeZoneInfo.DisplayName.ToString().Split(" ")[0]

            $today = Get-Date -Format g

            $title = 'Microsoft 365 License Availability Report'
            $html = @()
            $html += '<html><head><title>' + $title + '</title>'
            $html += '<style type="text/css">'
            $html += $css
            $html += '</style></head>'
            $html += '<body>'
            #table headers
            $html += '<table id="tbl">'
            $html += '<tr><td class="head"> </td></tr>'
            $html += '<tr><th class="section">Microsoft 365 Licenses</th></tr>'
            $html += '<tr><td class="head"><b>' + $($Organization.DisplayName) + '</b><br>' + $today + ' ' + $tz + '</td></tr>'
            $html += '<tr><td class="head"> </td></tr>'
            # $html += '<tr><td class="head"> </td></tr>'
            $html += '</table>'
            $html += '<tr><td class="head" colspan="4"></td></tr>'
            $html += '</table>'
            $html += '<table id="legend">'
            $html += '<tr><td class="Normal" width="60px">Normal</td><td class="Warning" width="60px">Warning</td><td class="Ignore" width="60px">Ignore</td></tr>'
            $html += '</table>'
            $html += '<table id="tbl">'
            $html += '<tr><td width="420px" colspan="2">Name</th><td width="170px">Quantity</td><td width="5px"></td></tr>'

            foreach ($item in ($skuCollection | Sort-Object ThresholdStatus -Descending)) {
                $html += '<tr><td><img src="data:image/png;base64,' + $logo + '"></img></td>'
                $html += '<th>' + $item.SkuName + '</th>'
                $html += '<td><b>' + $('{0:N0}' -f $item.Available) + ' available</b><br>' + $('{0:N0}' -f $item.Assigned) + ' assigned out of ' + $('{0:N0}' -f $item.Total) #+ ' total'

                $html += '<td class="' + ($item.ThresholdStatus) + '" width="5px"></td></tr>'
            }

            $html += '<tr><td class="head" colspan="4"></td></tr>'
            $html += '</table>'

            $html += '<table id="legend">'
            $html += '<tr><td class="Normal" width="60px">Normal</td><td class="Warning" width="60px">Warning</td><td class="Ignore" width="60px">Ignore</td></tr>'
            $html += '</table>'
            $html += '<table id="settings">'
            $html += '<tr><td colspan="2"><a href="' + $ThisModule.ProjectURI + '">' + $ThisModule.Name + ' v' + $ThisModule.Version + '</a></td></tr>'
            $html += '</table>'
            $html += '</body>'
            $html += '</html>'
            $html = ($html -join "`n")
        }

        if ($OutputType -eq 'Html') {
            $html
        }

        if ($OutputType -eq 'PSObject') {
            $skuCollection
        }

        if ($OutputType -eq 'Email') {
            $ResourceFolder = [System.IO.Path]::Combine((Split-Path ($ThisModule.Path) -Parent), 'resource')
            $logo = $([convert]::ToBase64String([System.IO.File]::ReadAllBytes("$resourceFolder\logo.png")))
            $Subject = "[$($Organization.DisplayName)] Microsoft 365 License Availability"
            $mailBody = @{
                message = @{
                    subject                = $Subject
                    body                   = @{
                        content     = $($html.Replace("data:image/png;base64,$logo", "cid:logo"))
                        contentType = "HTML"
                    }
                    internetMessageHeaders = @(
                        @{
                            name  = "X-Mailer"
                            value = "PsGraphMail by june.castillote@gmail.com"
                        }
                    )
                    attachments            = @(
                        @{
                            "@odata.type"  = "#microsoft.graph.fileAttachment"
                            "contentID"    = "logo"
                            "name"         = "logo"
                            "IsInline"     = $true
                            "contentType"  = "image/png"
                            "contentBytes" = $logo
                        }
                    )
                }
            }

            # To recipients
            if ($To) {
                $mailBody.message += @{
                    toRecipients = @(
                        $(ConvertRecipientsToJSON $To)
                    )
                }
            }

            # Cc recipients
            if ($CC) {
                $mailBody.message += @{
                    ccRecipients = @(
                        $(ConvertRecipientsToJSON $CC)
                    )
                }
            }

            # BCC recipients
            if ($BCC) {
                $mailBody.message += @{
                    bccRecipients = @(
                        $(ConvertRecipientsToJSON $BCC)
                    )
                }
            }

            try {
                Send-MgUserMail -UserId $From -BodyParameter $mailBody
            }
            catch {
                SayError "Send email failed: $($_.Exception.Message)"
            }
        }
    }
}