Public/ResourceAccount/Get-TeamsResourceAccount.ps1
# Module: TeamsFunctions # Function: ResourceAccount # Author: David Eberhardt # Updated: 01-MAR-2022 # Status: Live function Get-TeamsResourceAccount { <# .SYNOPSIS Returns Resource Accounts from AzureAD .DESCRIPTION Returns one or more Resource Accounts based on input. This runs Get-CsOnlineApplicationInstance but reformats the Output with friendly names .PARAMETER UserPrincipalName Default and positional. One or more UserPrincipalNames to be queried. .PARAMETER DisplayName Optional. Search parameter. Alternative to Find-TeamsResourceAccount Use Find-TeamsUserVoiceConfig for more search options .PARAMETER ApplicationType Optional. Returns all Call Queues or AutoAttendants .PARAMETER PhoneNumber Optional. Returns all ResourceAccount with a specific string in the PhoneNumber .EXAMPLE Get-TeamsResourceAccount Returns all Resource Accounts. Depending on size of the Tenant, this might take a while. .EXAMPLE Get-TeamsResourceAccount -Identity ResourceAccount@TenantName.onmicrosoft.com Returns the Resource Account with the Identity specified, if found. .EXAMPLE Get-TeamsResourceAccount -DisplayName "Queue" Returns all Resource Accounts with "Queue" as part of their Display Name. Use Find-TeamsResourceAccount / Find-CsOnlineApplicationInstance for finer search .EXAMPLE Get-TeamsResourceAccount -ApplicationType AutoAttendant Returns all Resource Accounts of the specified ApplicationType. .EXAMPLE Get-TeamsResourceAccount -PhoneNumber +1555123456 Returns the Resource Account with the Phone Number specified, if found. .INPUTS None System.String .OUTPUTS System.Object .NOTES Pipeline input possible Running the CmdLet without any input might take a while, depending on size of the Tenant. .COMPONENT TeamsResourceAccount TeamsAutoAttendant TeamsCallQueue .FUNCTIONALITY Returns one or more Resource Accounts from the Tenant .LINK https://github.com/DEberhardt/TeamsFunctions/tree/master/docs/Get-TeamsResourceAccount.md .LINK https://github.com/DEberhardt/TeamsFunctions/tree/master/docs/about_TeamsResourceAccount.md .LINK https://github.com/DEberhardt/TeamsFunctions/tree/master/docs/ #> [CmdletBinding(SupportsPaging, DefaultParameterSetName = 'Identity')] [Alias('Get-TeamsRA')] [OutputType([System.Object])] param ( [Parameter(Position = 0, ParameterSetName = 'Identity', ValueFromPipeline, ValueFromPipelineByPropertyName, HelpMessage = 'User Principal Name of the Object.')] [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(ParameterSetName = 'DisplayName', ValueFromPipelineByPropertyName, HelpMessage = 'Searches for AzureAD Object with this Name')] [ValidateLength(3, 255)] [string]$DisplayName, [Parameter(ParameterSetName = 'AppType', HelpMessage = 'Limits search to specific Types: CallQueue or AutoAttendant')] [ValidateSet('CallQueue', 'AutoAttendant', 'CQ', 'AA')] [Alias('Type')] [string]$ApplicationType, [Parameter(ParameterSetName = 'Number', ValueFromPipelineByPropertyName, HelpMessage = 'Telephone Number of the Object')] [ValidateScript( { If ( $_ -match $script:TFMatchNumber ) { $True } else { throw [System.Management.Automation.ValidationMetadataException] 'Value must be a valid phone number. E.164 format expected, min 4 digits, but multiple formats accepted. Extensions will be stripped' } })] [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:StepsID1 = Get-WriteBetterProgressSteps -Code $($MyInvocation.MyCommand.Definition) -MaxId 1 $private:ActivityID0 = $($MyInvocation.MyCommand.Name) [int] $private:CountID0 = [int] $private:CountID1 = 1 #Initialising splatting Object $GetCsApplicationInstanceParameters = @{ WarningAction = 'SilentlyContinue' ErrorAction = 'SilentlyContinue' } if ( $PSBoundParameters.ContainsKey('First') ) { $GetCsApplicationInstanceParameters.First = $PSCmdlet.PagingParameters.First } if ( $PSBoundParameters.ContainsKey('Skip') ) { $GetCsApplicationInstanceParameters.Skip = $PSCmdlet.PagingParameters.Skip } if ( $PSBoundParameters.ContainsKey('IncludeTotalCount') ) { $GetCsApplicationInstanceParameters.IncludeTotalCount = $PSCmdlet.PagingParameters.IncludeTotalCount } #Worker Function function GetResourceAccounts { [CmdletBinding()] [OutputType([System.Object])] param( [Parameter(Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName, HelpMessage = 'Resource Account object')] [AllowNull()] [object]$ResourceAccount ) #param begin { $Stack = Get-PSCallStack } process { [int] $private:CountID0 = 1 $ActivityID0 = "$($Stack[1].FunctionName)" $StatusID0 = "'$($ResourceAccount.UserPrincipalName)'" $CurrentOperationID0 = 'Parsing ApplicationType' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 # readable Application type if ($PSBoundParameters.ContainsKey('ApplicationType')) { $ResourceAccountApplicationType = $ApplicationType } else { $ResourceAccountApplicationType = GetApplicationTypeFromAppId $ResourceAccount.ApplicationId } # Parsing CsOnlineUser $CurrentOperationID0 = 'Parsing Online Voice Routing Policy' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 try { $CsOnlineUser = $null $CsOnlineUser = Get-CsOnlineUser -Identity "$($ResourceAccount.UserPrincipalName)" -WarningAction SilentlyContinue -ErrorAction Stop } catch { Write-Verbose -Message "$StatusID0 - Parsing: Online Voice Routing Policy FAILED. CsOnlineUser not found" -Verbose } # Parsing TeamsUserLicense $CurrentOperationID0 = 'Parsing License Assignments' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 try { $ResourceAccountLicenseDetail = Get-AzureADUserLicenseDetail -ObjectId "$($ResourceAccount.UserPrincipalName)" -WarningAction SilentlyContinue -ErrorAction STOP if ( $ResourceAccountLicenseDetail ) { $ResourceAccountLicense = Get-UserLicensesFromLicenseDetailObject -UserLicenseDetail $ResourceAccountLicenseDetail @args } } catch { Write-Verbose -Message "$StatusID0 - License Query: No licenses assigned to this account" } # 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 $ResourceAccount.PhoneNumber) { try { $TeamsPhoneNumberObject = Get-CsPhoneNumberAssignment -TelephoneNumber $($ResourceAccount.PhoneNumber | Format-StringForUse -As Number) -ErrorAction Stop $ResourceAccountPhoneNumberType = switch ($TeamsPhoneNumberObject.PstnPartnerName) { 'Microsoft' { 'CallingPlan' } '' { 'DirectRouting' } Default { 'OperatorConnect' } } } catch { Write-Verbose -Message "$StatusID0 - Phone Number Query: No phone number found with Get-CsPhoneNumberAssignment" } } # Calling Line Identity $CurrentOperationID0 = 'Parsing Calling Line Identity' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 try { $CallingLineIdentity = Get-TeamsCLI -Identity $ResourceAccount.UserPrincipalName -ErrorAction Stop } catch { Write-Verbose -Message "$StatusID0 - Calling Line Identity: No CLI found with Get-TeamsCLI" } # Associations $CurrentOperationID0 = 'Parsing Association' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 $Association = Get-CsOnlineApplicationInstanceAssociation -Identity $ResourceAccount.ObjectId -WarningAction SilentlyContinue -ErrorAction SilentlyContinue if ( $Association ) { $AssociationObject = switch ($Association.ConfigurationType) { 'CallQueue' { Get-CsCallQueue -Identity $Association.ConfigurationId -WarningAction SilentlyContinue -ErrorAction SilentlyContinue } 'AutoAttendant' { Get-CsAutoAttendant -Identity $Association.ConfigurationId -WarningAction SilentlyContinue -ErrorAction SilentlyContinue } } $AssociationStatus = Get-CsOnlineApplicationInstanceAssociationStatus -Identity $ResourceAccount.ObjectId -WarningAction SilentlyContinue -ErrorAction SilentlyContinue } else { Write-Verbose -Message "$StatusID0 - Association: No Association found with Get-CsOnlineApplicationInstanceAssociation" $AssociationObject = $null $AssociationStatus = $null } # creating new PS Object (synchronous with Get and Set) $ResourceAccountObject = [PSCustomObject][ordered]@{ PSTypeName = 'PowerShell.TeamsFunctsions.ResourceAccount' ObjectId = $ResourceAccount.ObjectId UserPrincipalName = $ResourceAccount.UserPrincipalName DisplayName = $ResourceAccount.DisplayName ApplicationType = $ResourceAccountApplicationType InterpretedUserType = $CsOnlineUser.InterpretedUserType UsageLocation = $CsOnlineUser.UsageLocation License = $($ResourceAccountLicense.ProductName -join ',') PhoneNumberType = $ResourceAccountPhoneNumberType PhoneNumber = $ResourceAccount.PhoneNumber OnlineVoiceRoutingPolicy = $CsOnlineUser.OnlineVoiceRoutingPolicy CallingLineIdentity = $CallingLineIdentity.Description AssociatedTo = $AssociationObject.Name AssociatedAs = $Association.ConfigurationType AssociationStatus = $AssociationStatus.Status } # Output Write-Progress -Id 0 -Activity $ActivityID0 -Completed Write-Output $ResourceAccountObject } end { } } } #begin process { Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand)" [int] $private:CountID0 = 1 $ResourceAccounts = $null $StatusID0 = 'Information Gathering' #region Data gathering $CurrentOperationID0 = 'Querying Resource Accounts' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 try { if ($PSBoundParameters.ContainsKey('UserPrincipalName')) { # Default Parameterset [System.Collections.Generic.List[object]]$ResourceAccounts = @() foreach ($I in $UserPrincipalName) { $RA = $null Write-Verbose -Message "Querying Resource Account with UserPrincipalName '$I'" $RA = Get-CsOnlineApplicationInstance -Identity "$I" @GetCsApplicationInstanceParameters if ($RA) { [void]$ResourceAccounts.Add($RA) } else { Write-Verbose -Message "No Resource Account found with UserPrincipalName '$I'" } } } elseif ($PSBoundParameters.ContainsKey('DisplayName')) { # Minimum Character length is 3 Write-Verbose -Message "DisplayName - Searching for Accounts with DisplayName '$DisplayName'" $ResourceAccounts = Get-CsOnlineApplicationInstance @GetCsApplicationInstanceParameters | Where-Object -Property DisplayName -Like -Value "*$DisplayName*" } elseif ($PSBoundParameters.ContainsKey('ApplicationType')) { Write-Verbose -Message "ApplicationType - Searching for Accounts with ApplicationType '$ApplicationType'" $AppId = GetAppIdFromApplicationType $ApplicationType $ResourceAccounts = Get-CsOnlineApplicationInstance @GetCsApplicationInstanceParameters | Where-Object -Property ApplicationId -EQ -Value $AppId } elseif ($PSBoundParameters.ContainsKey('PhoneNumber')) { $SearchString = Format-StringForUse "$($PhoneNumber.split(';')[0].split('x')[0])" -SpecialChars 'telx:+() -' Write-Verbose -Message "PhoneNumber - Searching for normalised PhoneNumber '$SearchString'" $ResourceAccounts = Get-CsOnlineApplicationInstance @GetCsApplicationInstanceParameters | Where-Object -Property PhoneNumber -Like -Value "*$SearchString*" } else { Write-Information 'INFO: Resource Account: Listing UserPrincipalName only. To query individual items, please provide Identity' Get-CsOnlineApplicationInstance @GetCsApplicationInstanceParameters | Select-Object UserPrincipalName return } } catch { if ($_.Exception.Message.Contains('RBAC')) { Write-Warning -Message 'AzureAd Admin Roles are not assigned, activated or have timed out. Please check your Administrative Roles' } Write-Error "$($_.Exception.Message)" Write-Information "INFO: Resource Account '$I' - Not found!" } # Stop script if no data has been determined if ($ResourceAccounts.Count -eq 0) { Write-Verbose -Message 'No Data found.' return } #endregion # Output Write-Verbose -Message "[PROCESS] Processing found Resource Accounts: $($ResourceAccounts.Count)" foreach ($ResourceAccount in $ResourceAccounts) { Write-Output (GetResourceAccounts -ResourceAccount $ResourceAccount @Args) } } #process end { Write-Verbose -Message "[END ] $($MyInvocation.MyCommand)" } #end } #Get-TeamsResourceAccount |