Public/ResourceAccount/Set-TeamsResourceAccount.ps1
# Module: TeamsFunctions # Function: ResourceAccount # Author: David Eberhardt # Updated: 01-OCT-2020 # Status: Live function Set-TeamsResourceAccount { <# .SYNOPSIS Changes a new Resource Account .DESCRIPTION This function allows you to update Resource accounts for Teams Call Queues and Auto Attendants. It can carry a license and optionally also a phone number. This Function was designed to service the ApplicationInstance in AD, the corresponding AzureAD User and its license and enable use of a phone number, all with one Command. .PARAMETER UserPrincipalName Required. Identifies the Object being changed .PARAMETER DisplayName Optional. The Name it will show up as in Teams. Invalid characters are stripped from the provided string .PARAMETER ApplicationType CallQueue or AutoAttendant. Determines the association the account can have: A resource Account of the type "CallQueue" can only be associated with to a Call Queue A resource Account of the type "AutoAttendant" can only be associated with an Auto Attendant The type can be switched later (this is supported and worked flawlessly when testing, but not recommended by Microsoft). .PARAMETER UsageLocation Two Digit Country Code of the Location of the entity. Before a License can be assigned, the account needs a Usage Location populated. .PARAMETER License Specifies the License to be assigned: PhoneSystem or PhoneSystem_VirtualUser If not provided, will default to PhoneSystem_VirtualUser Unlicensed Objects can exist, but cannot be assigned a phone number If a license already exists, it will try to swap the license to the specified one. PhoneSystem is an add-on license and cannot be assigned on its own. it has therefore been deactivated for now. .PARAMETER PhoneNumber Changes the Phone Number of the object. Can either be a Microsoft Number or a Direct Routing Number. Requires the Resource Account to be licensed correctly Required format is E.164, starting with a '+' and 10-15 digits long. .PARAMETER OnlineVoiceRoutingPolicy Optional. Required for DirectRouting. Assigns an Online Voice Routing Policy to the Account .PARAMETER Sync Calls Sync-CsOnlineApplicationInstance cmdlet after applying settings synchronizing the application instances from Azure Active Directory into Agent Provisioning Service. .PARAMETER PassThru By default, no output is generated, PassThru will display the Object changed .PARAMETER Force Optional. If parameter PhoneNumber is provided, will always remove the PhoneNumber from the object If PhoneNumber is not Null or Empty, will reapply the PhoneNumber .EXAMPLE Set-TeamsResourceAccount -UserPrincipalName ResourceAccount@TenantName.onmicrosoft.com -Displayname "My {ResourceAccount}" Will normalize the Display Name (i.E. remove special characters), then set it as "My ResourceAccount" .EXAMPLE Set-TeamsResourceAccount -UserPrincipalName AA-Mainline@TenantName.onmicrosoft.com -UsageLocation US Sets the UsageLocation for the Account in AzureAD to US. .EXAMPLE Set-TeamsResourceAccount -UserPrincipalName AA-Mainline@TenantName.onmicrosoft.com -License PhoneSystem_VirtualUser Requires the Account to have a UsageLocation populated. Applies the License to Resource Account AA-Mainline. If no license is assigned, will try to assign. If the license is already applied, no action is currently taken. .EXAMPLE Set-TeamsResourceAccount -UserPrincipalName AA-Mainline@TenantName.onmicrosoft.com -PhoneNumber +1555123456 Changes the Phone number of the Object. Will cleanly remove the Phone Number first before reapplying it. This will only succeed if the object is licensed correctly! .EXAMPLE Set-TeamsResourceAccount -UserPrincipalName AA-Mainline@TenantName.onmicrosoft.com -PhoneNumber $Null Removes the Phone number from the Object .EXAMPLE Set-TeamsResourceAccount -UserPrincipalName MyRessourceAccount@TenantName.onmicrosoft.com -ApplicationType AutoAttendant Switches MyResourceAccount to the Type AutoAttendant Though working correctly in all tests, please handle with care .INPUTS System.String .OUTPUTS System.Void - Default Behavior System.Object - With Switch PassThru .NOTES Though working correctly in all tests, please handle with care when changing Application Types Existing Application Instance Objects may get corrupted when treated as a User. If in doubt, please recreate the Resource Account and retire the old object. At the moment, swapping licenses is not possible/implemented. Please address manually in the Admin Center .COMPONENT TeamsResourceAccount TeamsAutoAttendant TeamsCallQueue .FUNCTIONALITY Changes a resource Account in AzureAD for use in Teams .LINK https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/Set-TeamsResourceAccount.md .LINK https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/about_TeamsResourceAccount.md .LINK https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/ #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] [Alias('Set-TeamsRA')] [OutputType([System.Void])] param ( [Parameter(Mandatory, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName, HelpMessage = 'UPN of the Object to change')] [Alias('Identity', 'ObjectId')] [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 = 'Display Name is shown in Teams')] [string]$DisplayName, [Parameter(HelpMessage = 'CallQueue or AutoAttendant')] [ValidateSet('CallQueue', 'AutoAttendant', 'CQ', 'AA')] [Alias('Type')] [string]$ApplicationType, [Parameter(HelpMessage = 'Usage Location to assign')] [ValidateScript( { if ($_ -in $(&$global:TfAcSbTwoLetterCountryCode)) { $True } else { throw [System.Management.Automation.ValidationMetadataException] 'Value must be a valid ISO3166-alpha2 Two-Letter CountryCode. Use Intellisense for options' } })] [ArgumentCompleter({ &$global:TfAcSbTwoLetterCountryCode })] [string]$UsageLocation, [Parameter(HelpMessage = 'License to be assigned')] [ValidateScript( { if ($_ -in $(&$global:TfAcSbAzureAdLicense)) { return $true } else { throw [System.Management.Automation.ValidationMetadataException] 'Value must be a valid License. Use Intellisense for options or Get-AzureAdLicense (ParameterName)' } })] [ArgumentCompleter({ &$global:TfAcSbAzureAdLicense })] [string[]]$License, [Parameter(ValueFromPipelineByPropertyName, HelpMessage = 'Telephone Number to assign')] [Alias('Tel', 'Number', 'TelephoneNumber')] [AllowNull()] [AllowEmptyString()] [string]$PhoneNumber, [Parameter(ValueFromPipelineByPropertyName, HelpMessage = 'Name of the Online Voice Routing Policy')] [Alias('OVP')] [ValidateScript( { if ($_ -in $(&$global:TfAcSbVoiceRoutingPolicy)) { return $true } else { throw [System.Management.Automation.ValidationMetadataException] 'Value must be a valid Policy in the Tenant. Use Intellisense for options' } })] [ArgumentCompleter({ &$global:TfAcSbVoiceRoutingPolicy })] [string]$OnlineVoiceRoutingPolicy, [Parameter(HelpMessage = 'By default, no output is generated, PassThru will display the Object changed')] [switch]$PassThru, [Parameter(ValueFromPipelineByPropertyName, HelpMessage = 'Synchronizes Resource Account with the Agent Provisioning Service')] [switch]$Sync, [Parameter(Mandatory = $false)] [switch]$Force ) #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('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' } if ( $PSBoundParameters.ContainsKey('ErrorAction')) { $InformationPreference = $PSCmdlet.SessionState.PSVariable.GetValue('ErrorAction') } else { $ErrorActionPreference = 'Stop' } #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 # Enabling $Confirm to work with $Force if ($Force -and -not $Confirm) { $ConfirmPreference = 'None' } #region Validating Licenses to be applied result in correct Licensing (contain PhoneSystem) $PlansToTest = 'MCOEV_VIRTUALUSER', 'MCOEV' if ( $PSBoundParameters.ContainsKey('License') ) { $ActivityID0 = 'Preparation' $StatusID0 = 'Verifying input' $CurrentOperationID0 = 'Validating Licenses to be applied result in correct Licensing' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 $IncludesPlan = 0 foreach ($L in $License) { foreach ($PlanToTest in $PlansToTest) { $Included = Test-AzureAdLicenseContainsServicePlan -License "$L" -ServicePlan "$PlanToTest" if ($Included) { $IncludesPlan++ Write-Verbose -Message "License '$L' ServicePlan '$PlanToTest' - Included: OK" } else { Write-Verbose -Message "License '$L' ServicePlan '$PlanToTest' - NOT included" } } } if ( $IncludesPlan -lt 1 ) { Write-Warning -Message "ServicePlan validation - None of the Licenses include any of the required ServicePlans '$PlansToTest' - Account may not be operational!" } } #endregion #TODO Create Worker function to apply, rework as other CmdLets } #begin process { Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand)" foreach ($UPN in $UserPrincipalName) { $ActivityID0 = "'$UPN'" #region PREPARATION $CountID0 = 1 $StatusID0 = 'Verifying input' #region Lookup of UserPrincipalName $CurrentOperationID0 = 'Querying Object' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 try { #Trying to query the Resource Account $Object = (Get-CsOnlineApplicationInstance -Identity "$UPN" -WarningAction SilentlyContinue -ErrorAction STOP) $CurrentDisplayName = $Object.DisplayName Write-Verbose -Message "'$UPN' OnlineApplicationInstance found: '$CurrentDisplayName'" } catch { # Catching anything Write-Error -Message "'$UPN' OnlineApplicationInstance not found!" -Category ObjectNotFound -RecommendedAction 'Please provide a valid UserPrincipalName of an existing Resource Account' #-ErrorAction Stop return } #endregion #region Normalising $DisplayName $CurrentOperationID0 = 'Processing DisplayName' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 if ($PSBoundParameters.ContainsKey('DisplayName')) { $DisplayNameNormalised = Format-StringForUse -InputString $DisplayName -As DisplayName $Name = $DisplayNameNormalised Write-Verbose -Message "DisplayName normalised to: '$Name'" } else { $Name = $CurrentDisplayName } #endregion #region ApplicationType and Associations $CurrentOperationID0 = 'Parsing Application Type' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 if ($PSBoundParameters.ContainsKey('ApplicationType')) { # Translating $ApplicationType (Name) to ID used by Commands. $AppId = GetAppIdFromApplicationType $ApplicationType $CurrentAppId = $Object.ApplicationId # Does the ApplicationType differ? Does it have to be changed? if ($AppId -eq $CurrentAppId) { # Application IDs match - Type does not need to be changed Write-Verbose -Message "'$Name ($UPN)' Application Type already set to: $ApplicationType" } else { # Finding all Associations to of this Resource Account to Call Queues or Auto Attendants $Associations = Get-CsOnlineApplicationInstanceAssociation -Identity "$UPN" -WarningAction SilentlyContinue -ErrorAction Ignore if ($Associations.count -gt 0) { # Associations found. Aborting Write-Error -Message "'$Name ($UPN)' ApplicationType cannot be changed! Object is associated with Call Queue or AutoAttendant." -Category OperationStopped -RecommendedAction 'Remove Associations with Remove-CsOnlineApplicationInstanceAssociation manually' -ErrorAction Stop } else { Write-Verbose -Message "'$Name ($UPN)' Application Type will be changed to: $ApplicationType" } } } #endregion #region PhoneNumber $CurrentOperationID0 = 'Parsing Phone Number' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 # Querying CurrentPhoneNumber try { $CurrentPhoneNumber = $Object.PhoneNumber.Replace('tel:', '') Write-Verbose -Message "'$Name ($UPN)' Phone Number assigned currently: '$CurrentPhoneNumber'" } catch { $CurrentPhoneNumber = $null Write-Verbose -Message "'$Name ($UPN)' Phone Number assigned currently: NONE" } #endregion #region UsageLocation $CurrentOperationID0 = 'Parsing Usage Location' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 $CurrentUsageLocation = (Get-AzureADUser -ObjectId "$UPN" -WarningAction SilentlyContinue).UsageLocation if ($PSBoundParameters.ContainsKey('UsageLocation')) { if ($Usagelocation -eq $CurrentUsageLocation) { Write-Verbose -Message "'$Name ($UPN)' Usage Location already set to: $CurrentUsageLocation" } else { Write-Verbose -Message "'$Name ($UPN)' Usage Location will be set to: $Usagelocation" } } else { if ($null -ne $CurrentUsageLocation) { Write-Verbose -Message "'$Name ($UPN)' Usage Location currently set to: $CurrentUsageLocation" $UsageLocation = $CurrentUsageLocation } else { if (($PSBoundParameters.ContainsKey('License')) -or ($PSBoundParameters.ContainsKey('PhoneNumber'))) { Write-Error -Message "'$Name ($UPN)' Usage Location not set!" -Category ObjectNotFound -RecommendedAction 'Please run command again and specify -UsageLocation'# -ErrorAction Stop return } else { Write-Warning -Message "'$Name ($UPN)' Usage Location not set! This is a requirement for License assignment and Phone Number" } } } #endregion #region Current License $CurrentOperationID0 = 'Querying current License and Testing Licensing Scope (Should contain PhoneSystem or PhoneSystemVirtualUser)' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 $IsLicensed = $false # Determining license Status of Object $UserLicense = Get-AzureAdUserLicense -Identity "$UPN" if ( $UserLicense.PhoneSystem -or $UserLicense.PhoneSystemVirtualUser ) { if ( $UserLicense.PhoneSystemStatus -eq 'Success' ) { Write-Verbose -Message "'$Name ($UPN)' PhoneSystem is assigned and enabled successfully" $IsLicensed = $true } } else { Write-Verbose -Message "'$Name ($UPN)' PhoneSystem present: NONE" } #endregion #endregion #region ACTION $StatusID0 = 'Applying Settings' #region DisplayName if ($PSBoundParameters.ContainsKey('DisplayName')) { $CurrentOperationID0 = 'Setting Display Name' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 try { if ($PSCmdlet.ShouldProcess("$UPN", "Set-CsOnlineApplicationInstance -Displayname `"$DisplayNameNormalised`"")) { Write-Verbose -Message "'$CurrentDisplayName' Changing DisplayName to: $DisplayNameNormalised" $null = (Set-CsOnlineApplicationInstance -Identity "$UPN" -DisplayName "$DisplayNameNormalised" -ErrorAction STOP) Write-Information "SUCCESS: Displayname changed to '$DisplayName'" $CurrentDisplayName = $Object.DisplayName } } catch { Write-Verbose -Message 'FAILED - Error encountered changing DisplayName' Write-Error -Message 'Problem encountered with changing DisplayName' -Category NotImplemented -Exception $_.Exception -RecommendedAction 'Try manually with Set-CsOnlineApplicationInstance' Write-Debug $_ } } #endregion #region Application Type if ($PSBoundParameters.ContainsKey('ApplicationType')) { $CurrentOperationID0 = 'Checking Application Type is correct' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 # Application Type Change? if ($AppId -ne $CurrentAppId) { try { if ($PSCmdlet.ShouldProcess("$UPN", "Set-CsOnlineApplicationInstance -ApplicationId $AppId")) { Write-Verbose -Message "'$Name ($UPN)' Setting Application Type to: $ApplicationType" $null = (Set-CsOnlineApplicationInstance -Identity "$UPN" -ApplicationId $AppId -ErrorAction STOP) Write-Verbose -Message 'SUCCESS' } } catch { Write-Error -Message 'Problem encountered changing Application Type' -Category NotImplemented -Exception $_.Exception -RecommendedAction 'Try manually with Set-CsOnlineApplicationInstance' Write-Debug $_ } } } #endregion #region UsageLocation if ($PSBoundParameters.ContainsKey('UsageLocation')) { $CurrentOperationID0 = 'Setting Usage Location' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 if ($PSCmdlet.ShouldProcess("$UPN", "Set-AzureADUser -UsageLocation $UsageLocation")) { try { Set-AzureADUser -ObjectId "$UPN" -UsageLocation $UsageLocation -ErrorAction STOP Write-Verbose -Message "'$Name ($UPN)' SUCCESS - Usage Location set to: $UsageLocation" } catch { if ($PSBoundParameters.ContainsKey('License')) { Write-Error -Message "'$Name ($UPN)' Usage Location could not be set. Please apply before applying license" -Category NotSpecified -RecommendedAction 'Apply manually, then run Set-TeamsResourceAccount to apply license and phone number' } else { Write-Warning -Message "'$Name ($UPN)' Usage Location cannot be set. If a license is needed, please assign UsageLocation beforehand" } } } } #endregion #region Licensing $StatusID0 = 'Applying Settings - License' if ($PSBoundParameters.ContainsKey('License')) { $CurrentOperationID0 = 'Processing License assignment' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 if ( $License -in $UserLicense.Licenses.ParameterName -and $IsLicensed ) { # No action required Write-Information "INFO: Resource Account '$Name ($UPN)' License '$License' already assigned." $IsLicensed = $true } else { try { if ($PSCmdlet.ShouldProcess("$UPN", "Set-TeamsUserLicense -Add $License")) { $null = (Set-TeamsUserLicense -Identity "$UPN" -Add $License -ErrorAction STOP) Write-Information "INFO: Resource Account '$Name' License assignment - '$License' SUCCESS" $IsLicensed = $true } } catch { Write-Error -Message "'$Name' License assignment failed for '$License' with Exception: '$($_.Exception.Message)'" } } } #endregion #region Waiting for License Application if ($PSBoundParameters.ContainsKey('License') -and $PSBoundParameters.ContainsKey('PhoneNumber')) { $CurrentOperationID0 = $StatusID0 = '' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 $i = 0 $iMax = 600 Write-Warning -Message "Applying a License may take longer than provisioned for ($($iMax/60) mins) in this Script - If so, please apply PhoneNumber manually with Set-TeamsResourceAccount" Write-Verbose -Message "License '$License'- Expecting one of the corresponding ServicePlans '$PlansToTest'" $ActivityID1 = 'Checking License propagation as a requirement before applying Phone Number' $StatusID1 = 'Azure Active Directory is propagating Object. Please wait' $CurrentOperationID1 = 'Waiting for Test-TeamsUserLicense to return a positive Result' do { if ($i -gt $iMax) { Write-Error -Message "Could not find Successful Provisioning Status of ServicePlan '$PlansToTest' in AzureAD in the last $iMax Seconds" -Category LimitsExceeded -RecommendedAction 'Please verify License has been applied correctly (Get-TeamsResourceAccount); Continue with Set-TeamsResourceAccount' -ErrorAction Stop } Write-Progress -Id 1 -ParentId 0 -Activity $ActivityID1 -Status $StatusID1 -CurrentOperation $CurrentOperationID1 -SecondsRemaining $($iMax - $i) -PercentComplete (($i * 100) / $iMax) Start-Sleep -Milliseconds 1000 $i++ $AllTests = $false $AllTests = foreach ($PlanToTest in $PlansToTest) { Test-TeamsUserLicense -Identity "$UPN" -ServicePlan "$PlanToTest" } $TeamsUserLicenseAssigned = if ( ($AllTests) -contains $true ) { $true } else { $false } } while (-not $TeamsUserLicenseAssigned) Write-Progress -Id 1 -Activity $ActivityID1 -Completed } #endregion #region PhoneNumber $StatusID0 = 'Applying Settings - Phone Number' if ($PSBoundParameters.ContainsKey('PhoneNumber')) { # Removing old Number (if $null or different to current) if ( $null -eq $PhoneNumber ) { $CurrentOperationID0 = 'Removing Phone Number' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 Write-Verbose -Message "'$Name ($UPN)' ACTION: Removing Phone Number" # Removing Phone Number with Set-TeamsPhoneNumber try { $SetTeamsPhoneNumber = @{ 'UserPrincipalName' = "$UPN" 'PhoneNumber' = $null 'WarningAction' = 'SilentlyContinue' 'ErrorAction' = 'Stop' 'Force' = $false } if ($PSBoundParameters.ContainsKey('Debug') -or $DebugPreference -eq 'Continue') { "Function: $($MyInvocation.MyCommand.Name) - Parameters (SetTeamsPhoneNumber)", ($SetTeamsPhoneNumber | Format-Table -AutoSize | Out-String).Trim() | Write-Debug } Set-TeamsPhoneNumber @SetTeamsPhoneNumber $StatusMessage = "Number removed from $ObjectType" Write-Information "SUCCESS: '$UPN' - $CurrentOperationID0`: OK - $StatusMessage" } catch { $ErrorLogMessage = "'$UPN' - $CurrentOperationID0`: Failed: '$($_.Exception.Message)'" Write-Error -Message $ErrorLogMessage } if ($PSBoundParameters.ContainsKey('Debug') -or $DebugPreference -eq 'Continue') { "Function: $($MyInvocation.MyCommand.Name)", (Get-CsOnlineApplicationInstance -Identity "$UPN" | Select-Object UserPrincipalName, DisplayName, PhoneNumber | Format-Table -AutoSize | Out-String).Trim() | Write-Debug } } else { Write-Verbose -Message "'$Name ($UPN)' No Number assigned" } # Assigning Telephone Number $CurrentOperationID0 = 'Applying Phone Number' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 if ($PhoneNumber) { if ( -not $IsLicensed ) { Write-Error -Message 'A Phone Number can only be assigned to licensed objects.' -Category ResourceUnavailable -RecommendedAction 'Please apply a license before assigning the number. Set-TeamsResourceAccount can be used to do both' } else { Write-Verbose -Message "'$Name ($UPN)' ACTION: Assigning Phone Number" # Applying Phone Number with Set-TeamsPhoneNumber try { $SetTeamsPhoneNumber = @{ 'UserPrincipalName' = "$UPN" 'PhoneNumber' = "$PhoneNumber" 'WarningAction' = 'SilentlyContinue' 'ErrorAction' = 'Stop' 'Force' = $false } if ($PSBoundParameters.ContainsKey('Debug') -or $DebugPreference -eq 'Continue') { "Function: $($MyInvocation.MyCommand.Name) - Parameters (SetTeamsPhoneNumber)", ($SetTeamsPhoneNumber | Format-Table -AutoSize | Out-String).Trim() | Write-Debug } Set-TeamsPhoneNumber @SetTeamsPhoneNumber $StatusMessage = "Number assigned to $ObjectType`: '$PhoneNumber'" Write-Information "SUCCESS: '$UPN' - $CurrentOperationID0`: OK - $StatusMessage" } catch { $ErrorLogMessage = "'$UPN' - $CurrentOperationID0`: Failed: '$($_.Exception.Message)'" Write-Error -Message $ErrorLogMessage } } if ($PSBoundParameters.ContainsKey('Debug') -or $DebugPreference -eq 'Continue') { "Function: $($MyInvocation.MyCommand.Name)", (Get-CsOnlineApplicationInstance -Identity "$UPN" | Select-Object UserPrincipalName, DisplayName, PhoneNumber | Format-Table -AutoSize | Out-String).Trim() | Write-Debug } } } #endregion #region OnlineVoiceRoutingPolicy $CurrentOperationID0 = 'Applying Online Voice Routing Policy' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 if ( $OnlineVoiceRoutingPolicy ) { try { Grant-CsOnlineVoiceRoutingPolicy -Identity $UPN -PolicyName $OnlineVoiceRoutingPolicy -ErrorAction Stop Write-Information "SUCCESS: '$Name ($UPN)' Assigning OnlineVoiceRoutingPolicy: OK: '$OnlineVoiceRoutingPolicy'" } catch { $ErrorLogMessage = "User '$Name ($UPN)' Assigning OnlineVoiceRoutingPolicy`: Failed: '$($_.Exception.Message)'" Write-Error -Message $ErrorLogMessage } } #endregion # Synchronisation if ( $PSBoundParameters.ContainsKey('Sync') ) { Write-Verbose -Message "Switch 'Sync' - Resource Account is synchronised with Agent Provisioning Service" $null = Sync-CsOnlineApplicationInstance -ObjectId $ResourceAccount.ObjectId -Force Write-Information 'SUCCESS: Synchronising Resource Account with Agent Provisioning Service' } #endregion $RAObject = $null if ( $PassThru ) { $StatusID0 = 'Output' $CurrentOperationID0 = 'Querying Object' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 $RAObject = Get-TeamsResourceAccount -Identity "$UPN" } Write-Progress -Id 0 -Activity $ActivityID0 -Completed Write-Output $RAObject } } #process end { Write-Verbose -Message "[END ] $($MyInvocation.MyCommand)" } #end } #Set-TeamsResourceAccount |