Public/Licensing/Get-AzureAdUserLicense.ps1

# Module: TeamsFunctions
# Function: Licensing
# Author: David Eberhardt
# Updated: 01-APR-2020
# Status: Live




function Get-AzureAdUserLicense {
  <#
  .SYNOPSIS
    Returns License information for an Object in AzureAD
  .DESCRIPTION
    Returns an Object containing all Licenses found for a specific Object
    Licenses and ServicePlans are nested in the respective parameters for further investigation
  .PARAMETER UserPrincipalname
    The UserPrincipalname or ObjectId of the Object.
  .PARAMETER FilterRelevantForTeams
    Filters the output and displays only Licenses relevant to Teams
  .EXAMPLE
    Get-AzureAdUserLicense [-UserPrincipalname] John@domain.com
 
    Displays all licenses assigned to User John@domain.com
  .EXAMPLE
    Get-AzureAdUserLicense -UserPrincipalname John@domain.com,Jane@domain.com
 
    Displays all licenses assigned to Users John@domain.com and Jane@domain.com
  .EXAMPLE
    Get-AzureAdUserLicense -UserPrincipalname Jane@domain.com -FilterRelevantForTeams
 
    Displays all relevant Teams licenses assigned to Jane@domain.com
  .EXAMPLE
    Import-Csv User.csv | Get-AzureAdUserLicense
 
    Displays all licenses assigned to Users from User.csv, Column UserPrincipalname, ObjectId or Identity.
    The input file must have a single column heading of "UserPrincipalname" with properly formatted UPNs.
  .INPUTS
    System.String
  .OUTPUTS
    System.Object
  .NOTES
    Requires a connection to Azure Active Directory
  .COMPONENT
    Licensing
  .FUNCTIONALITY
    Returns a list of Licenses assigned to a specific User depending on input
  .LINK
    https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/Get-AzureAdUserLicense.md
  .LINK
    https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/about_Licensing.md
  .LINK
    https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/about_UserManagement.md
  .LINK
    https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/
  #>


  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '', Justification = 'Required for performance. Removed with Disconnect-Me')]
  [CmdletBinding()]
  [OutputType([PSCustomObject])]
  param(
    [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, HelpMessage = 'Enter the UPN or login name of the user account, typically <user>@<domain>.')]
    [Alias('ObjectId', 'Identity')]
    [ValidateScript( {
        If ($_ -match '@' -or $_ -match $script:TFMatchGuid) { $True } else {
          throw [System.Management.Automation.ValidationMetadataException] 'Value must be a valid UPN or ObjectId'
        } })]
    [string[]]$UserPrincipalName,

    [Parameter(HelpMessage = 'Displays only Licenses relevant to Teams')]
    [switch]$FilterRelevantForTeams
  ) #param

  begin {
    Show-FunctionStatus -Level Live
    $Stack = Get-PSCallStack
    $Called = ($stack.length -ge 3)
    Write-Verbose -Message "[BEGIN ] $($MyInvocation.MyCommand)"

    # Asserting AzureAD Connection
    if ( -not $script:TFPSSA) { $script:TFPSSA = Assert-AzureADConnection; if ( -not $script:TFPSSA ) { break } }

    # Setting Preference Variables according to Upstream settings
    if (-not $PSBoundParameters.ContainsKey('Verbose')) { $VerbosePreference = $PSCmdlet.SessionState.PSVariable.GetValue('VerbosePreference') }
    if (-not $PSBoundParameters.ContainsKey('Confirm')) { $ConfirmPreference = $PSCmdlet.SessionState.PSVariable.GetValue('ConfirmPreference') }
    if (-not $PSBoundParameters.ContainsKey('WhatIf')) { $WhatIfPreference = $PSCmdlet.SessionState.PSVariable.GetValue('WhatIfPreference') }
    if (-not $PSBoundParameters.ContainsKey('Debug')) { $DebugPreference = $PSCmdlet.SessionState.PSVariable.GetValue('DebugPreference') } else { $DebugPreference = 'Continue' }
    if ( $PSBoundParameters.ContainsKey('InformationAction')) { $InformationPreference = $PSCmdlet.SessionState.PSVariable.GetValue('InformationAction') } else { $InformationPreference = 'Continue' }

    # preparing Output Field Separator
    $OFS = ', ' # do not remove - Automatic variable, used to separate elements!

  } #begin

  process {
    Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand)"
    foreach ($User in $UserPrincipalName) {
      try {
        $UserObject = $null
        $UserLicenseDetail = $null
        #New Graph Equivalent to be rolled out through Refactor in Orbit
        #$UserObject = Get-MgUserLicenseDetail -UserId "$User" -WarningAction SilentlyContinue -ErrorAction STOP
        $UserObject = Get-AzureADUser -ObjectId "$User" -WarningAction SilentlyContinue -ErrorAction STOP
        #New Graph Equivalent to be rolled out through Refactor in Orbit
        #$UserLicenseDetail = Get-MgUserLicenseDetail -UserId "$User" -WarningAction SilentlyContinue -ErrorAction STOP
        $UserLicenseDetail = Get-AzureADUserLicenseDetail -ObjectId "$User" -WarningAction SilentlyContinue -ErrorAction STOP
      }
      catch {
        #Write-Error -Message "Error ocurred for User '$User': $($_.Exception.Message)" -Category InvalidResult
        throw $_
        continue
      }

      [string]$DisplayName = $UserObject.DisplayName

      if ( $UserLicenseDetail ) {
        # Querying Licenses
        $UserLicenses = Get-UserLicensesFromLicenseDetailObject -UserLicenseDetail $UserLicenseDetail @args

        # Querying Service Plans
        $UserServicePlans = Get-UserServicePlansFromLicenseDetailObject -UserLicenseDetail $UserLicenseDetail @args
      }

      # Filter
      if ( $FilterRelevantForTeams.IsPresent ) {
        $UserLicenses = $UserLicenses | Where-Object { $_.IncludesTeams -or $_.IncludesPhoneSystem }
        $UserServicePlans = $UserServicePlans | Where-Object RelevantForTeams
      }

      # Determining Phone System Status
      $PhoneSystemLicense = ('MCOEV' -in $UserServicePlans.ServicePlanName)
      $PhoneSystemVirtual = ('MCOEV_VIRTUALUSER' -in $UserServicePlans.ServicePlanName)
      if ( $PhoneSystemLicense ) {
        $PhoneSystemProvisioningStatus = $UserServicePlans | Where-Object ServicePlanName -EQ 'MCOEV'
        if ( $PhoneSystemProvisioningStatus.Count -gt 1 ) {
          # PhoneSystem assigned more than once!
          Write-Warning -Message "User '$User' Multiple assignments found for PhoneSystem. Please verify License assignment."
          $PhoneSystemStatus = ($PhoneSystemProvisioningStatus | Select-Object -ExpandProperty ProvisioningStatus) -join ', '

        }
        else {
          $PhoneSystemStatus = $PhoneSystemProvisioningStatus.ProvisioningStatus
        }

      }
      elseif ( $PhoneSystemVirtual ) {
        $PhoneSystemStatus = ($UserServicePlans | Where-Object ServicePlanName -EQ 'MCOEV_VIRTUALUSER').ProvisioningStatus
      }
      else {
        $PhoneSystemStatus = 'Unassigned'
      }

      # Calling Plans
      $currentCallingPlan = ($UserLicenses | Where-Object LicenseType -EQ 'CallingPlan').ProductName

      # Output
      $output = [PSCustomObject][ordered]@{
        PSTypeName               = 'PowerShell.TeamsFunctsions.AzureAdUserLicense'
        UserPrincipalName        = $UserObject.UserPrincipalName
        DisplayName              = $DisplayName
        #UserId = $UserObject.Id
        ObjectId                 = $UserObject.ObjectId
        UsageLocation            = $UserObject.UsageLocation
        Licenses                 = $UserLicenses
        ServicePlans             = $UserServicePlans
        AudioConferencing        = $('MCOMEETADV' -in $UserServicePlans.ServicePlanName)
        CommonAreaPhoneLicense   = $('MCOCAP' -in $UserLicenses.SkuPartNumber)
        PhoneSystemVirtualUser   = $PhoneSystemVirtual
        PhoneSystem              = $PhoneSystemLicense
        PhoneSystemStatus        = $PhoneSystemStatus
        CallingPlanDomestic120   = $('MCOPSTN5' -in $UserServicePlans.ServicePlanName -or 'MCOPSTN_5' -in $UserServicePlans.ServicePlanName)
        CallingPlanDomestic      = $('MCOPSTN1' -in $UserServicePlans.ServicePlanName -or 'MCOPSTN_1' -in $UserServicePlans.ServicePlanName) # also covering MCOPSTN_1_GOV
        CallingPlanInternational = $('MCOPSTN2' -in $UserServicePlans.ServicePlanName -or 'MCOPSTNEAU2' -in $UserServicePlans.ServicePlanName)
        OtherCallingPlan         = $('MCOPSTNEAU2' -in $UserServicePlans.ServicePlanName -or 'BUSINESS_VOICE_MED2_TELCO' -in $UserServicePlans.ServicePlanName)
        CommunicationsCredits    = $('MCOPSTNC' -in $UserServicePlans.ServicePlanName)
        CallingPlan              = $currentCallingPlan
      }

      # Adding Script Method to Licenses and ServicePlans
      if ( $output.Licenses ) {
        $output.Licenses | Add-Member -MemberType ScriptMethod -Name ToString -Value { $this.ProductName } -Force
      }
      if ( $output.ServicePlans ) {
        $output.ServicePlans | Add-Member -MemberType ScriptMethod -Name ToString -Value { $this.ProductName } -Force
      }

      Write-Output $output
    }
  } #process

  end {
    Write-Verbose -Message "[END ] $($MyInvocation.MyCommand)"
  } #end
} #Get-AzureAdUserLicense