Public/UserManagement/TeamsCallableEntity/Get-TeamsCallableEntity.ps1
# Module: TeamsFunctions # Function: Teams Auto Attendant # Author: David Eberhardt # Updated: 01-NOV-2020 # Status: Live function Get-TeamsCallableEntity { <# .SYNOPSIS Returns a callable Entity Object from an Identity/ObjectId or string .DESCRIPTION Determines an Objects validity for use in an Auto Attendant or Call Queue Prepares output of Get-CsCallQueue by querying the Team and Channel (used in Get-TeamsCallQueue) Prepares output of Get-CsAutoAttendant (nested Objects) for display (used in Get-TeamsAutoAttendant) Returns a custom Object mimiking a CallableEntity Object, returning Entity, Identity & Type .PARAMETER Identity The ObjectId of the CallableEntity linked .EXAMPLE Get-TeamsCallableEntity -Identity "My Group Name" Queries whether "My Group Name" can be found as an AzureAdUser, AzureAdGroup or CsOnlineApplicationInstance. .EXAMPLE Get-TeamsCallableEntity -Identity "John@domain.com","MyResourceAccount@domain.com" Queries whether John or MyResourceAccount can be found as an AzureAdUser, AzureAdGroup or CsOnlineApplicationInstance. .EXAMPLE Get-TeamsCallableEntity -Identity 00000000-0000-0000-0000-000000000000 Queries whether the provided ObjectId can be found as an AzureAdUser, AzureAdGroup or CsOnlineApplicationInstance. .EXAMPLE Get-TeamsCallableEntity -Identity "1 (555) 1234-567" No Queries performed, number is normalised into a LineURI then passed on as the Tel URI. Returns a custom Object mimiking a CallableEntity Object, returning Entity, Identity & Type .EXAMPLE Get-TeamsCallableEntity -Identity "tel:+15551234567" No Queries performed, as the Tel URI is passed on as-is. Returns a custom Object mimiking a CallableEntity Object, returning Entity, Identity & Type .EXAMPLE Get-TeamsCallableEntity -Identity "00000000-0000-0000-0000-000000000000\19:abcdef1234567890abcdef1234567890@thread.tacv2" Format provided is of in TeamId\ChannelId. This is interpreted as a TeamsChannel. Queries Team & Channel. Returns a custom Object mimiking a CallableEntity Object, returning Entity, Identity & Type .EXAMPLE Get-TeamsCallableEntity -Identity "My Team Name\My Channel Name" Format provided is of in TeamDisplayName\ChannelDisplayName. This is interpreted as a TeamsChannel. Queries Team & Channel. Returns a custom Object mimiking a CallableEntity Object, returning Entity, Identity & Type .INPUTS System.String .OUTPUTS System.Object .NOTES If a match for Team\Channel or PhoneNumber is found, these are treated as such. For Team\Channel, the Id and DisplayName are interchangeable. The first match is performed for '\', if it matches, the string is split and individual matches are performed for Team and Channel respectively. The PhoneNumber is found with a very flexible match based on multiple formats (Integer, E.164 or LineUri) If no match is found, queries the string sequentially against AzureAdUser, CsOnlineApplicationInstance and AzureAdGroup. Returns a custom Object mimiking a CallableEntity Object, returning Entity, Identity & Type This script is used to determine the eligibility of an Object as a Callable Entity in Call Queues and Auto Attendants This script does not yet support Announcements (sorry. Working on it) This script does not support the Types for legacy Hunt Group or Organisational Auto Attendant If nothing can be found for the String, an Object is returned with the Entity being $null .COMPONENT UserManagement TeamsAutoAttendant TeamsCallQueue .FUNCTIONALITY Queries a Callable Entity attached to a Call Queues or Auto Attendants .LINK https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/Get-TeamsCallableEntity.md .LINK https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/about_TeamsAutoAttendant.md .LINK https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/about_TeamsCallQueue.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, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName, HelpMessage = 'Identity of the Callable Entity')] [Alias('ObjectId', 'UserPrincipalName')] [string[]]$Identity ) #param begin { Show-FunctionStatus -Level Live Write-Verbose -Message "[BEGIN ] $($MyInvocation.MyCommand)" # Asserting AzureAD Connection if ( -not $script:TFPSSA) { $script:TFPSSA = Assert-AzureADConnection; if ( -not $script:TFPSSA ) { break } } # Asserting MicrosoftTeams Connection if ( -not (Assert-MicrosoftTeamsConnection) ) { break } # Setting Preference Variables according to Upstream settings if (-not $PSBoundParameters.ContainsKey('Verbose')) { $VerbosePreference = $PSCmdlet.SessionState.PSVariable.GetValue('VerbosePreference') } 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' } } #begin process { Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand)" foreach ($Id in $Identity) { Write-Verbose -Message "Processing '$Id'" if ($Id -match '\\') { try { $Team, $Channel = $null $Team, $Channel = Get-TeamsTeamAndChannel -String "$Id" -ErrorAction Stop if ($Channel) { Write-Verbose 'Target is a Teams Channel' $TeamAndChannelName = "$($Team.DisplayName)" + '\' + "$($Channel.DisplayName)" $CallableEntity = [TFCallableEntity]::new( "$TeamAndChannelName", "$($Channel.Id)", 'Channel', 'Channel') } else { $CallableEntity = [TFCallableEntity]::new( "$Id", $null, 'Unknown', $null ) } } catch { $CallableEntity = [TFCallableEntity]::new( "$Id", $null, 'Unknown', $null ) } } elseif ($Id -match $script:TFMatchNumber -and -not ($Id -match '@')) { Write-Verbose 'Target is a Tel URI' $Id = Format-StringForUse -InputString "$Id" -As LineURI $CallableEntity = [TFCallableEntity]::new( "$Id", "$Id", 'TelURI', 'ExternalPstn') } else { Write-Verbose 'Target is not a Tel URI' try { # FIRST: Trying an AzureAdUser for User or ResourceAccount (ApplicationEndPoint) $CallTarget = Get-AzureADUser -ObjectId "$Id" -WarningAction SilentlyContinue -ErrorAction Stop Write-Verbose 'Target is a User or ResourceAccount (ApplicationEndpoint)' if ( $CallTarget ) { try { $null = Get-CsOnlineApplicationInstance -Identity "$($CallTarget.ObjectId)" -WarningAction SilentlyContinue -ErrorAction Stop Write-Verbose 'Target is a ResourceAccount (ApplicationEndpoint)' $CallableEntity = [TFCallableEntity]::new( "$($CallTarget.UserPrincipalName)", "$($CallTarget.ObjectId)", 'ResourceAccount', 'ApplicationEndpoint') } catch { Write-Verbose 'Target is a User' $CallableEntity = [TFCallableEntity]::new( "$($CallTarget.UserPrincipalName)", "$($CallTarget.ObjectId)", 'User', 'User') # Re-querying the CsOnlineUser to obtain the SIP address - may not be needed # This was tried to ascertain why CQ Users fail to be added if their SIP Address is different than their UPN but did not work. # Further investigation is needed #$CsUser = Get-CsOnlineUser -Identity "$($CallTarget.ObjectId)" -WarningAction SilentlyContinue -ErrorAction Stop #$CallableEntity = [TFCallableEntity]::new( "$($CsUser.SipAddress)", "$($CallTarget.ObjectId)", 'User', 'User') } } else { Write-Verbose 'Target is not a User or a ResourceAccount (ApplicationEndpoint)' throw } } catch { # Not a User, not a ResourceAccount (ApplicationEndPoint) Write-Verbose 'Target is not a User or a ResourceAccount (ApplicationEndpoint)' try { # SECOND: Trying a AzureAdGroup for SharedVoicemail $CallTarget = $null $CallTarget = Get-AzureADGroup -SearchString "$Id" -WarningAction SilentlyContinue -ErrorAction SilentlyContinue if (-not $CallTarget ) { try { $CallTarget = Get-AzureADGroup -ObjectId "$Id" -WarningAction SilentlyContinue -ErrorAction Stop } catch { Write-Information 'INFO: Performing Search... finding ALL Groups' if ( -not $global:TeamsFunctionsTenantAzureAdGroups) { Write-Verbose -Message 'Groups not loaded yet, depending on the size of the Tenant, this will run for a while!' -Verbose $global:TeamsFunctionsTenantAzureAdGroups = Get-AzureADGroup -All $true -WarningAction SilentlyContinue -ErrorAction SilentlyContinue } if ($Id -match '@') { $CallTarget = $global:TeamsFunctionsTenantAzureAdGroups | Where-Object Mail -EQ "$Id" -WarningAction SilentlyContinue -ErrorAction SilentlyContinue } else { $CallTarget = $global:TeamsFunctionsTenantAzureAdGroups | Where-Object DisplayName -EQ "$Id" -WarningAction SilentlyContinue -ErrorAction SilentlyContinue } } } else { Write-Verbose 'Target is a Group' } # dealing with potential duplicates if ( $CallTarget.Count -gt 1 ) { Write-Verbose 'Target is a Group, but multiple Groups found' $CallTarget = $CallTarget | Where-Object DisplayName -EQ "$Id" } if ( $CallTarget.Count -gt 1 ) { Write-Verbose 'Target is a Group, but not unique!' throw [System.Reflection.AmbiguousMatchException]::New('Multiple Targets found - Result not unique (Group)') } else { # Unique result found if ( $CallTarget ) { $CallableEntity = [TFCallableEntity]::new( "$($CallTarget.DisplayName)", "$($CallTarget.ObjectId)", 'Group', 'SharedVoicemail') } else { throw } } } catch [System.Reflection.AmbiguousMatchException] { Write-Error -Message "No Unique Target found for '$Id'" -Exception System.Reflection.AmbiguousMatchException -ErrorAction Stop } catch { Write-Warning -Message 'The Object is not supported as a Callable Entity for AutoAttendants or CallQueues' # Defaulting to Unknown $CallableEntity = [TFCallableEntity]::new( "$Id", $null, 'Unknown', $null ) } } } Write-Output $CallableEntity } } #process end { Write-Verbose -Message "[END ] $($MyInvocation.MyCommand)" } #end } # Get-TeamsCallableEntity |