Public/UserManagement/TeamsCommonAreaPhone/Get-TeamsCommonAreaPhone.ps1
# Module: TeamsFunctions # Function: VoiceConfig # Author: David Eberhardt # Updated: 01-DEC-2020 # Status: Live function Get-TeamsCommonAreaPhone { <# .SYNOPSIS Returns Common Area Phones from AzureAD .DESCRIPTION Returns one or more AzureAdUser Accounts that are Common Area Phones Accounts returned are strictly limited to having to have the Common Area Phone License assigned. .PARAMETER UserPrincipalName Default and positional. One or more UserPrincipalNames to be queried .PARAMETER DisplayName Optional. Search parameter. Use Find-TeamsUserVoiceConfig for more search options .PARAMETER PhoneNumber Optional. Returns all Common Area Phones with a specific string in the PhoneNumber .EXAMPLE Get-TeamsCommonAreaPhone Returns all Common Area Phones. Depending on size of the Tenant, this might take a while. .EXAMPLE Get-TeamsCommonAreaPhone -Identity MyCAP@TenantName.onmicrosoft.com Returns the Common Area Phone with the Identity specified, if found. .EXAMPLE Get-TeamsCommonAreaPhone -DisplayName "Lobby" Returns all Common Area Phones with "Lobby" as part of their Display Name. .EXAMPLE Get-TeamsCommonAreaPhone -PhoneNumber +1555123456 Returns the Resource Account with the Phone Number specified, if found. .INPUTS System.String .OUTPUTS System.Object .NOTES Displays similar output as Get-TeamsUserVoiceConfig, but more tailored to Common Area Phones Running the CmdLet without any input might take a while, depending on size of the Tenant. .FUNCTIONALITY Queries a Common Area Phone in AzureAD for use in Teams .COMPONENT UserManagement .COMPONENT VoiceConfiguration .LINK https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/Get-TeamsCommonAreaPhone.md .LINK https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/about_VoiceConfiguration.md .LINK https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/about_UserManagement.md .LINK https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/ #> [CmdletBinding(ConfirmImpact = 'Low', DefaultParameterSetName = 'Identity')] [Alias('Get-TeamsCAP')] [OutputType([System.Object])] param( [Parameter(Position = 0, ParameterSetName = 'Identity', ValueFromPipeline, ValueFromPipelineByPropertyName, HelpMessage = 'UserPrincipalName of the User')] [Alias('ObjectId', 'Identity')] [string[]]$UserPrincipalName, [Parameter(ParameterSetName = 'DisplayName', ValueFromPipeline, ValueFromPipelineByPropertyName, HelpMessage = 'Searches for AzureAD Object with this Name')] [ValidateLength(3, 255)] [string]$DisplayName, [Parameter(ParameterSetName = 'Number', ValueFromPipelineByPropertyName, HelpMessage = 'Telephone Number of the Object')] [ValidateScript( { If ($_ -match $script:TFMatchNumber) { $True } else { throw [System.Management.Automation.ValidationMetadataException] 'Not a valid phone number. E.164 format expected, min 4 digits, but multiple formats accepted.' } })] [Alias('Tel', 'Number', 'TelephoneNumber')] [string]$PhoneNumber ) #param begin { Show-FunctionStatus -Level Live Write-Verbose -Message "[BEGIN ] $($MyInvocation.MyCommand)" Write-Verbose -Message "Need help? Online: $global:TeamsFunctionsHelpURLBase$($MyInvocation.MyCommand)`.md" # 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('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' } #Initialising Counters $private:StepsID0, $private:StepsID0 = Get-WriteBetterProgressSteps -Code $($MyInvocation.MyCommand.Definition) -MaxId 1 $private:ActivityID0 = $($MyInvocation.MyCommand.Name) [int] $private:CountID0 = [int] $private:CountID0 = 1 # Querying Global Policies $StatusID0 = 'Information Gathering' $CurrentOperationID0 = 'Querying Global Policies' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 if ( -not $script:TFTenantInfoGlobalIPPhonePolicy) { $global:TFTenantInfoGlobalIPPhonePolicy = Get-CsTeamsIPPhonePolicy 'Global' } if ( -not $script:TFTenantInfoGlobalCallingPolicy) { $global:TFTenantInfoGlobalCallingPolicy = Get-CsTeamsCallingPolicy 'Global' } if ( -not $script:TFTenantInfoGlobalCallParkPolicy) { $global:TFTenantInfoGlobalCallParkPolicy = Get-CsTeamsCallParkPolicy 'Global' } #Worker Function function GetCommonAreaPhone { [CmdletBinding()] [OutputType([System.Object])] param( [Parameter(Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName, HelpMessage = 'Common Area Phone object')] [AllowNull()] [object]$CommonAreaPhone ) #param begin { $Stack = Get-PSCallStack } process { [int] $private:CountID0 = 1 $ActivityID0 = "$($Stack[1].FunctionName)" $StatusID0 = "'$($CommonAreaPhone.UserPrincipalName)'" #region Policies # TeamsIPPhonePolicy and CommonAreaPhoneSignIn $CurrentOperationID0 = 'Parsing IP Phone Policy (CommonAreaPhoneSignIn)' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 if ( -not $CommonAreaPhone.TeamsIPPhonePolicy ) { $UserSignInMode = $global:TFTenantInfoGlobalIPPhonePolicy.SignInMode if ( $global:TFTenantInfoGlobalIPPhonePolicy.SignInMode -ne 'CommonAreaPhoneSignIn' ) { Write-Warning -Message "$StatusID0 - TeamsIpPhonePolicy is not set. The Global policy does not have the Sign-in mode set to 'CommonAreaPhoneSignIn'. To enable Common Area phones to sign in with the best experience, please assign a TeamsIpPhonePolicy or change the Global Policy!" } else { Write-Verbose -Message "$StatusID0 - TeamsIpPhonePolicy is not set, but Global policy has set the Sign-in mode set to 'CommonAreaPhoneSignIn'." } } else { try { $UserIpPhonePolicy = $null $UserIpPhonePolicy = Get-CsTeamsIPPhonePolicy $([string]$CommonAreaPhone.TeamsIPPhonePolicy) -WarningAction SilentlyContinue -ErrorAction Stop $UserSignInMode = $UserIpPhonePolicy.SignInMode if ( $UserIpPhonePolicy.SignInMode -ne 'CommonAreaPhoneSignIn' ) { Write-Warning -Message "$StatusID0 - TeamsIpPhonePolicy '$($CommonAreaPhone.TeamsIPPhonePolicy)' is set, but the Sign-in mode set not set to 'CommonAreaPhoneSignIn'. To enable Common Area phones to sign in with the best experience, please change the TeamsIpPhonePolicy!" } else { Write-Verbose -Message "$StatusID0 - TeamsIpPhonePolicy '$($CommonAreaPhone.TeamsIPPhonePolicy)' is set and Sign-In Mode is set to 'CommonAreaPhoneSignIn'" } } catch { Write-Warning -Message "$StatusID0 - TeamsIpPhonePolicy '$($CommonAreaPhone.TeamsIPPhonePolicy)' cannot be queried. 'EffectiveSignInMode' not set" $UserSignInMode = $null } } # TeamsCallingPolicy and AllowPrivateCalling $CurrentOperationID0 = 'Parsing Teams Calling Policy (AllowPrivateCalling)' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 if ( -not $CommonAreaPhone.TeamsCallingPolicy ) { $UserAllowPrivateCalling = $global:TFTenantInfoGlobalCallingPolicy.AllowPrivateCalling if ( -not $global:TFTenantInfoGlobalCallingPolicy.AllowPrivateCalling ) { Write-Warning -Message "$StatusID0 - TeamsCallingPolicy is not set. The Global policy does not Allow Private Calling. To enable ANY calling functionality for this phone, please assign a TeamsCallingPolicy or change the Global Policy!" } else { Write-Verbose -Message "$StatusID0 - TeamsCallingPolicy is not set, but Global policy does Allow Private Calling." } } else { try { $UserTeamsCallingPolicy = $null $UserTeamsCallingPolicy = Get-CsTeamsCallingPolicy $([string]$CommonAreaPhone.TeamsCallingPolicy) -WarningAction SilentlyContinue -ErrorAction Stop $UserAllowPrivateCalling = $UserTeamsCallingPolicy.AllowPrivateCalling if ( -not $UserTeamsCallingPolicy.AllowPrivateCalling ) { Write-Warning -Message "$StatusID0 - TeamsCallingPolicy '$($CommonAreaPhone.TeamsCallingPolicy)' is set, but does not Allow Private Calling. To enable ANY calling functionality for this phone, please change the TeamsCallingPolicy!" } else { Write-Verbose -Message "$StatusID0 - TeamsCallingPolicy '$($CommonAreaPhone.TeamsCallingPolicy)' is set and does Allow Private Calling" } } catch { Write-Warning -Message "$StatusID0 - TeamsCallingPolicy '$($CommonAreaPhone.TeamsCallingPolicy)' cannot be queried. 'EffectiveAllowPrivateCalling' not set" $UserAllowPrivateCalling = $null } } # TeamsCallParkPolicy and AllowCallPark $CurrentOperationID0 = 'Parsing Teams Call Park Policy (AllowCallPark)' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 if ( -not $CommonAreaPhone.TeamsCallParkPolicy ) { $UserAllowCallPark = $global:TFTenantInfoGlobalCallParkPolicy.AllowCallPark if ( -not $global:TFTenantInfoGlobalCallParkPolicy.AllowCallPark ) { Write-Warning -Message "$StatusID0 - TeamsCallParkPolicy is not set. The Global policy does not allow Call Parking. To enable Call Parking for Common Area phones, please assign a TeamsCallParkPolicy or change the Global Policy!" } else { Write-Verbose -Message "$StatusID0 - TeamsCallParkPolicy is not set, but Global policy does allow Call Parking." } } else { try { $UserCallParkPolicy = $null $UserCallParkPolicy = Get-CsTeamsCallParkPolicy $([string]$CommonAreaPhone.TeamsCallParkPolicy) -WarningAction SilentlyContinue -ErrorAction Stop $UserAllowCallPark = $UserTeamsCallingPolicy.AllowCallPark if ( -not $UserCallParkPolicy.AllowCallPark ) { Write-Warning -Message "$StatusID0 - TeamsCallParkPolicy '$($CommonAreaPhone.TeamsCallParkPolicy)' is set, but does not allow Call Parking. To enable Call Parking, please change the TeamsCallParkPolicy!" } else { Write-Verbose -Message "$StatusID0 - TeamsCallParkPolicy '$($CommonAreaPhone.TeamsCallParkPolicy)' is set and does allow Call Parking" } } catch { Write-Warning -Message "$StatusID0 - TeamsCallParkPolicy '$($CommonAreaPhone.TeamsCallParkPolicy)' cannot be queried. 'EffectiveAllowCallPark' not set" $UserAllowCallPark = $null } } #endregion #region License and Number Type # Parsing TeamsUserLicense $CurrentOperationID0 = 'Parsing License Assignments' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 try { $CommonAreaPhoneLicenseDetail = Get-AzureADUserLicenseDetail -ObjectId "$($CommonAreaPhone.UserPrincipalName)" -WarningAction SilentlyContinue -ErrorAction STOP if ( $CommonAreaPhoneLicenseDetail ) { $CommonAreaPhoneLicense = Get-UserLicensesFromLicenseDetailObject -UserLicenseDetail $CommonAreaPhoneLicenseDetail @args } } catch { Write-Verbose -Message "$StatusID0 - License Query: No licenses assigned to this account" } $ProvisioningStatus = $($CommonAreaPhoneLicenseDetail.ServicePlans | Where-Object ServicePlanName -Like 'MCOEV*').ProvisioningStatus # Phone Number Type $CurrentOperationID0 = 'Parsing NumberType' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 if ($null -ne $CommonAreaPhone.LineUri) { try { $TeamsPhoneNumberObject = Get-CsPhoneNumberAssignment -TelephoneNumber $($CommonAreaPhone.LineUri | Format-StringForUse -As Number) -ErrorAction Stop $CommonAreaPhonePhoneNumberType = switch ($TeamsPhoneNumberObject.PstnPartnerName) { 'Microsoft' { 'CallingPlan' } 'DirectRouting' { 'DirectRouting' } '' { 'DirectRouting' } Default { 'OperatorConnect' } } } catch { Write-Verbose -Message "$StatusID0 - Phone Number Query: No phone number found with Get-CsPhoneNumberAssignment" } } #endregion #region Output - Creating new PS Object (synchronous with Get and Set) $CurrentOperationID0 = 'Preparing Output Object' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 $CommonAreaPhoneObject = [PSCustomObject][ordered]@{ PSTypeName = 'PowerShell.TeamsFunctsions.CommonAreaPhone' ObjectId = $CommonAreaPhone.Identity UserPrincipalName = $CommonAreaPhone.UserPrincipalName DisplayName = $CommonAreaPhone.DisplayName Description = $CommonAreaPhone.Description UsageLocation = $CommonAreaPhone.UsageLocation InterpretedUserType = $CommonAreaPhone.InterpretedUserType License = $($CommonAreaPhoneLicense.ProductName -join ',') PhoneSystem = $($CommonAreaPhone.FeatureTypes -contains 'PhoneSystem') PhoneSystemStatus = $ProvisioningStatus EnterpriseVoiceEnabled = $CommonAreaPhone.EnterpriseVoiceEnabled PhoneNumberType = $CommonAreaPhonePhoneNumberType PhoneNumber = $CommonAreaPhone.LineURI TenantDialPlan = $CommonAreaPhone.TenantDialPlan OnlineVoiceRoutingPolicy = $CommonAreaPhone.OnlineVoiceRoutingPolicy TeamsIPPhonePolicy = $CommonAreaPhone.TeamsIPPhonePolicy TeamsCallingPolicy = $CommonAreaPhone.TeamsCallingPolicy TeamsCallParkPolicy = $CommonAreaPhone.TeamsCallParkPolicy EffectiveSignInMode = $UserSignInMode EffectiveAllowPrivateCalling = $UserAllowPrivateCalling EffectiveAllowCallPark = $UserAllowCallPark } Write-Progress -Id 0 -Activity $ActivityID0 -Completed Write-Output $CommonAreaPhoneObject } end { } } } #begin process { Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand)" [int] $private:CountID0 = 1 $CommonAreaPhones = $null $StatusID0 = 'Information Gathering' #region Data gathering $CurrentOperationID0 = 'Querying Common Area Phones' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 switch ($PSCmdlet.ParameterSetName) { 'Identity' { # Default Parameterset [System.Collections.Generic.List[object]]$CommonAreaPhones = @() foreach ($User in $UserPrincipalName) { Write-Verbose -Message "Querying Resource Account with UserPrincipalName '$User'" try { $CAP = $null $CAP = Get-CsOnlineUser -Identity "$User" -WarningAction SilentlyContinue -ErrorAction Stop [void]$CommonAreaPhones.Add($CAP) } catch { # If CsOnlineUser not found, trying AzureAdUser try { $CAP = $null $CAP = Get-AzureADUser -ObjectId "$User" -WarningAction SilentlyContinue -ErrorAction Stop [void]$CommonAreaPhones.Add($CAP) Write-Warning -Message "User '$User' - found in AzureAd but not in Teams (CsOnlineUser)!" Write-Verbose -Message 'You receive this message if no License containing Teams is assigned or the Teams ServicePlan (TEAMS1) is disabled! Please validate the User License. No further validation is performed. The Object returned only contains data from AzureAd' -Verbose } catch [Microsoft.Open.AzureAD16.Client.ApiException] { Write-Error -Message "User '$User' not found in Teams (CsOnlineUser) nor in Azure Ad (AzureAdUser). Please validate UserPrincipalName. Exception message: Resource '$User' does not exist or one of its queried reference-property objects are not present." -Category ObjectNotFound continue } catch { Write-Error -Message "User '$User' not found. Error encountered: $($_.Exception.Message)" -Category ObjectNotFound continue } } } } 'Displayname' { # Minimum Character length is 3 Write-Verbose -Message "DisplayName - Searching for Accounts with DisplayName '$DisplayName'" $Filter = 'DisplayName -like "*{0}*"' -f $DisplayName $CommonAreaPhones = Get-CsOnlineUser -Filter $Filter -WarningAction SilentlyContinue -ErrorAction SilentlyContinue #$CommonAreaPhones = Get-CsOnlineUser -WarningAction SilentlyContinue | Where-Object -Property DisplayName -Like -Value "*$DisplayName*" } 'Number' { $SearchString = Format-StringForUse "$($PhoneNumber.split(';')[0].split('x')[0])" -SpecialChars 'telx:+() -' Write-Verbose -Message "PhoneNumber - Searching for normalised PhoneNumber '$SearchString'" #$Filter = 'LineURI -like "*{0}*"' -f $SearchString $Filter = 'LineURI -like "{0}*"' -f $SearchString $CommonAreaPhones = Get-CsOnlineUser -Filter $Filter -WarningAction SilentlyContinue -ErrorAction SilentlyContinue } Default { Write-Warning -Message 'No parameters provided. Please provide at least one UserPrincipalName with Identity a DisplayName or a PhoneNumber' return } } # Stop script if no data has been determined if ($CommonAreaPhones.Count -eq 0) { Write-Verbose -Message 'No Data found.' Write-Progress -Id 0 -Activity $ActivityID0 -Completed return } #endregion # Output Write-Verbose -Message "[PROCESS] Processing found Common Area Phones: $($CommonAreaPhones.Count)" foreach ($CommonAreaPhone in $CommonAreaPhones) { Write-Output (GetCommonAreaPhone -CommonAreaPhone $CommonAreaPhone @Args) } } #process end { Write-Verbose -Message "[END ] $($MyInvocation.MyCommand)" } #end } # Get-TeamsCommonAreaPhone |