Public/UserManagement/TeamsUserCallingSettings/Set-TeamsUserCallGroup.ps1
# Module: TeamsFunctions # Function: VoiceConfig # Author: David Eberhardt # Updated: 16-OCT-2022 # Status: RC function Set-TeamsUserCallGroup { <# .SYNOPSIS Sets CallingSettings (Call Group) for one or more Users .DESCRIPTION Adds, removes or sets Call Group Targets, Sets Call Group Order and Notification as well as Notification Override for one or more users. Call Group settings are applied to all users (removing targeting itself). This is useful to apply the same Call Group settings to multiple users .PARAMETER UserPrincipalName Required. UserPrincipalName (UPN) of the User(s) to set Call Group settings for. .PARAMETER CallGroupTargets ParameterSet Option. Replaces Call Group with the defined targets excluding self. .PARAMETER Add ParameterSet Option. Adds Call Group target to defined targets excluding self. .PARAMETER Remove ParameterSet Option. Removes Call Group target from defined targets. .PARAMETER RemoveAll ParameterSet Option. Removes Call Group entirely. .PARAMETER NotificationSetting Optional. Updates the NotificationSetting for all Members of this users Call Group. .PARAMETER GroupNotificationOverride Optional. Updates the NotificationSetting for this user in any CallGroup this user is a member in. .PARAMETER TargetInOrder Optional Switch. Only available for Call Groups with 5 members or less. If the Call Group has more than five members after prossing parameters Add, Remove or CallGroupTargets, this switch is ignored. The CallGroupOrder is set to Simultaneous in this case. .PARAMETER WriteErrorLog Optional. If Errors are encountered, writes log to C:\Temp .PARAMETER PassThru Optional. Displays object after applying settings .EXAMPLE Set-TeamsUserCallGroup [-UserPrincipalName] John@domain.com -RemoveAll -WriteErrorLog Removes the Call Group Targets from the User and removes the Group from being used. Redirection to the Group will be disabled if it is used as an UnansweredTarget or as an AlsoRing/ForwardingTarget If errors are encountered, will write an Error-Log to C:\Temp .EXAMPLE Set-TeamsUserCallGroup [-UserPrincipalName] John@domain.com -Add Jill@domain.com -Remove Jeff@domain.com Adds Jill as a member of John's Call Group and removes Jeff .EXAMPLE "John@domain.com" | Set-TeamsUserCallGroup -CallGroupTargets Jill@domain.com,Jeff@domain.com -TargetInOrder Replaces all members of John's Call Group (if any) and only adds Jill & Jeff. Also activates Call Group targeting in Order. InOrder is only available for Call Groups with 5 or less members. .EXAMPLE "John@domain.com","Jill@domain.com","Jeff@domain.com" | Set-TeamsUserCallGroup -CallGroupTargets Jill@domain.com,Jeff@domain.com,John@domain.com Replaces all members of John's, Jill's & Jeff's Call Group (if any) and only adds John's, Jill's & Jeff's. Users cannot be added to their own call group, so they will be removed. John will have Jeff & Jill as members, Jill will have John & Jeff and Jeff will have John & Jill. This is useful for bigger teams that all should have the same Call Group settings .EXAMPLE Set-TeamsUserCallGroup Jill@domain.com -NotificationSetting Banner Sets Calling Settings for Jill in ALL Call Groups Jill is a member in to Banner. This is not the same as the override .EXAMPLE Set-TeamsUserCallGroup Jeff@domain.com -GroupNotificationOverride Banner Sets Calling Settings for Jeff to override all Call Group Notifications for Jeff with Banner This overrides settings made by other userss in their Call Group as Jeff does not want to be rang for Call Group Calls. .INPUTS System.String .OUTPUTS System.Void System.Object .NOTES Wrapper for Set-CsUserCallingSettings that targets Call Group settings. For CallingSettings invoking AlsoRing, IfUnanswered or Forwarding please call Set-TeamsUserCallingSettings. .COMPONENT VoiceConfiguration .FUNCTIONALITY Applies User Calling Settings for Call Groups .LINK https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/Set-TeamsUserCallGroup.md .LINK https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/about_VoiceConfiguration.md .LINK https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/ #> [CmdletBinding( SupportsShouldProcess, DefaultParameterSetName = 'Set', PositionalBinding = $false )] [Alias('Set-TeamsUCG')] [OutputType([PSCustomObject])] param( [Parameter(Mandatory, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias('ObjectId', 'Identity', 'SipUri')] [ValidateScript( { If ($_ -match '@') { $True } else { throw [System.Management.Automation.ValidationMetadataException] 'Value must be a valid UPN' } })] [string[]]$UserPrincipalName, [Parameter(Mandatory, ParameterSetName = 'Set', HelpMessage = 'Targets for setting up a Call Group')] [ValidateScript( { If ($_ -match '@') { $True } else { throw [System.Management.Automation.ValidationMetadataException] 'Value must be a valid UPN' } })] [string[]]$CallGroupTargets, [Parameter(Mandatory, ParameterSetName = 'Add', HelpMessage = 'Targets for setting up a Call Group')] [Parameter(ParameterSetName = 'Remove', HelpMessage = 'Targets for setting up a Call Group')] [ValidateScript( { If ($_ -match '@') { $True } else { throw [System.Management.Automation.ValidationMetadataException] 'Value must be a valid UPN' } })] [string[]]$Add, [Parameter(Mandatory, ParameterSetName = 'Remove', HelpMessage = 'Targets for setting up a Call Group')] [Parameter(ParameterSetName = 'Add', HelpMessage = 'Targets for setting up a Call Group')] [ValidateScript( { If ($_ -match '@') { $True } else { throw [System.Management.Automation.ValidationMetadataException] 'Value must be a valid UPN' } })] [string[]]$Remove, [Parameter(Mandatory, ParameterSetName = 'RemoveAll', HelpMessage = 'Removes the CallGroup entirely')] [switch]$RemoveAll, [Parameter(HelpMessage = 'NotificationSetting for Call Group Targets')] [ValidateSet('Ring', 'Mute', 'Banner')] [string]$NotificationSetting, [Parameter(HelpMessage = 'NotificationSetting Override for all Call Groups this user is a member in')] [ValidateSet('Ring', 'Mute', 'Banner')] [Alias('NotificationOverride')] [string]$GroupNotificationOverride, [Parameter(ParameterSetName = 'Set', HelpMessage = 'Tries to apply CallGroupOrder InOrder rather than Simultaneous')] [Parameter(ParameterSetName = 'Add', HelpMessage = 'Tries to apply CallGroupOrder InOrder rather than Simultaneous')] [Parameter(ParameterSetName = 'Remove', HelpMessage = 'Tries to apply CallGroupOrder InOrder rather than Simultaneous')] [switch]$TargetInOrder, [Parameter(HelpMessage = 'No output is written by default, Switch PassThru will return changed object')] [switch]$PassThru, [Parameter(HelpMessage = 'Writes a Log File of Errors to C:\Temp')] [switch]$WriteErrorLog ) #param begin { Show-FunctionStatus -Level RC Write-Verbose -Message "[BEGIN ] $($MyInvocation.MyCommand)" # 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' } # Adding Types Add-Type -AssemblyName Microsoft.Open.AzureAD16.Graph.Client Add-Type -AssemblyName Microsoft.Open.Azure.AD.CommonLibrary # Worker Functions function AvailableCallGroupOrder ($User, $Count) { if ( $Count -gt 5 ) { Write-Warning -Message "User '$User' - CallGroupOrder 'InOrder' cannot be applied, too many CallGroupTargets detected. Applying 'Simultaneous'" return 'Simultaneous' } else { Write-Information "INFO: User '$User' - CallGroupOrder 'InOrder' can be applied ($Count Targets present)" return 'InOrder' } } } #begin process { Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand)" foreach ($User in $UserPrincipalName) { #region Querying Identity $UserCallingSettings = $null try { $UserCallingSettings = Get-CsUserCallingSettings -Identity $User -ErrorAction Stop } catch { if ( $_.Exception.Message.Contains('Could not resolve user') -or $_.Errordetails.Message.Contains('was not found') ) { $ErrorMessage = "'$User' - User not found" Write-Verbose $ErrorMessage } else { $ErrorMessage = "'$User' - Error: $($_.Errordetails.Message)" Write-Error -Message $ErrorMessage } if ( $WriteErrorLog.IsPresent ) { Write-TFErrorLog -ErrorLog $ErrorMessage -Artifact "$User" } continue } #endregion #region SETTINGS #region RemoveAll if ( $PSBoundParameters.ContainsKey('RemoveAll') ) { if ($PSCmdlet.ShouldProcess("Calling 'Set-CsUserCallingSettings' to REMOVE ALL CallGroupTargets for '$User'", "$User", 'Set-CsUserCallingSettings')) { #TEST REMOVE Command requires -IsUnansweredEnabled to be FALSE? - Run if needed. if ( $UserCallingSettings.UnansweredTargetType -eq 'Group' ) { $null = Set-CsUserCallingSettings -Identity "$User" -IsUnansweredEnabled $false Write-Verbose -Message "User '$User' - IsUnansweredEnabled was set to 'Group', This setting was reset" } if ( $UserCallingSettings.ForwardingTargetType -eq 'Group' ) { $null = Set-CsUserCallingSettings -Identity "$User" -IsForwardingEnabled $false Write-Verbose -Message "User '$User' - IsForwardingEnabled was set to 'Group', This setting was reset" } try { $CsUserCallingSettings = @{ CallGroupOrder = $UserCallingSettings.CallGroupOrder CallGroupTargets = @() ErrorAction = 'Stop' } $null = Set-CsUserCallingSettings -Identity "$User" @CsUserCallingSettings Write-Information "INFO: User '$User' - CallGroupTargets removed; Call Group reset" } catch { $ErrorLog = $_ Write-Error -Message $ErrorLog if ( $WriteErrorLog.IsPresent ) { Write-TFErrorLog -ErrorLog $ErrorLog -Artifact "$User" } } } continue } #endregion #region Set if ( $PSBoundParameters.ContainsKey('CallGroupTargets') ) { Write-Verbose -Message "User '$User' - CallGroupTargets - Testing Call Targets to be added" -Verbose #$VerifiedCallGroupTargets = [Management.Automation.PSSerializer]::DeSerialize([Management.Automation.PSSerializer]::Serialize($CallGroupTargets)) $VerifiedCallGroupTargets = @() $CallGroupTargets | ForEach-Object { $Target = $_ if ( $user -eq $($Target -replace 'sip:', '') ) { Write-Verbose -Message "User '$User' - CallGroupTargets - Cannot add itself as a Call Target - omitting Target" -Verbose } else { try { if ($Target | Test-TeamsUserVoiceConfig -ErrorAction Stop) { $CallTarget = $(if ( $Target -notmatch '^sip:') { 'sip:' }) + $Target $VerifiedCallGroupTargets += $CallTarget Write-Verbose -Message "User '$User' - Target '$Target' is eligible as a call target and will be added" } else { throw } } catch { $ErrorMessage = "User '$User' - Target '$Target' is not eligible as a call target - omitting Target" Write-Warning -Message $ErrorMessage if ( $WriteErrorLog.IsPresent ) { Write-TFErrorLog -ErrorLog $ErrorMessage -Artifact "$User" } } } } $CallGroupOrder = if ( $PSBoundParameters.ContainsKey('TargetInOrder') ) { AvailableCallGroupOrder $User $VerifiedCallGroupTargets.Count } else { $UserCallingSettings.CallGroupOrder } if ($PSCmdlet.ShouldProcess("Calling 'Set-CsUserCallingSettings' to SET CallGroupTargets for '$User'", "$User", 'Set-CsUserCallingSettings')) { try { $CsUserCallingSettings = @{ CallGroupOrder = $CallGroupOrder CallGroupTargets = $VerifiedCallGroupTargets ErrorAction = 'Stop' } $null = Set-CsUserCallingSettings -Identity "$User" @CsUserCallingSettings Write-Information "INFO: User '$User' - CallGroupTargets set" } catch { $ErrorLog = $_ Write-Error -Message $ErrorLog if ( $WriteErrorLog.IsPresent ) { Write-TFErrorLog -ErrorLog $ErrorLog -Artifact "$User" } } } } #endregion #region Add & Remove # Preparing UserCallGroupTargets Object for Add & Remove if ( $PSBoundParameters.ContainsKey('Add') -or $PSBoundParameters.ContainsKey('Remove') ) { $UserCallGroupTargets = { $UserCallingSettings.CallGroupTargets }.Invoke() if ($PSBoundParameters.ContainsKey('Debug') -or $DebugPreference -eq 'Continue') { "Function: $($MyInvocation.MyCommand.Name) - UserCallGroupTargets", ( $UserCallGroupTargets | Format-List | Out-String).Trim() | Write-Debug } } #region Add if ( $PSBoundParameters.ContainsKey('Add') ) { Write-Verbose -Message "User '$User' - Add - Testing Call Targets to be added" -Verbose $Add | ForEach-Object { $Target = $_ if ( $user -eq $($Target -replace 'sip:', '') ) { Write-Verbose -Message "User '$User' - Add - Cannot add itself as a Call Target - omitting Target" -Verbose } else { try { if ($Target | Test-TeamsUserVoiceConfig -ErrorAction Stop) { $CallTarget = $(if ( $Target -notmatch '^sip:') { 'sip:' }) + $Target if ( $CallTarget -notin $UserCallGroupTargets ) { [void]$UserCallGroupTargets.Add($CallTarget) Write-Verbose -Message "User '$User' - Target '$Target' is eligible as a call target and will be added" } else { Write-Verbose -Message "User '$User' - Target '$Target' already part of CallGroupTargets" -Verbose } } else { throw } } catch { $ErrorMessage = "User '$User' - Target '$Target' is not eligible as a call target - omitting Target" Write-Warning -Message $ErrorMessage if ( $WriteErrorLog.IsPresent ) { Write-TFErrorLog -ErrorLog $ErrorMessage -Artifact "$User" } } } } if ($PSBoundParameters.ContainsKey('Debug') -or $DebugPreference -eq 'Continue') { "Function: $($MyInvocation.MyCommand.Name) - UserCallGroupTargets after ADD", ( $UserCallGroupTargets | Format-List | Out-String).Trim() | Write-Debug } $CallGroupOrder = if ( $PSBoundParameters.ContainsKey('TargetInOrder') ) { AvailableCallGroupOrder $User $UserCallGroupTargets.Count } else { $UserCallingSettings.CallGroupOrder } if ($PSCmdlet.ShouldProcess("Calling 'Set-CsUserCallingSettings' to ADD CallGroupTargets for '$User'", "$User", 'Set-CsUserCallingSettings')) { try { $CsUserCallingSettings = @{ CallGroupOrder = $CallGroupOrder CallGroupTargets = $UserCallGroupTargets ErrorAction = 'Stop' } $null = Set-CsUserCallingSettings -Identity "$User" @CsUserCallingSettings Write-Information "INFO: User '$User' - CallGroupTargets added" } catch { $ErrorLog = $_ Write-Error -Message $ErrorLog if ( $WriteErrorLog.IsPresent ) { Write-TFErrorLog -ErrorLog $ErrorLog -Artifact "$User" } } } } #endregion #region Remove if ( $PSBoundParameters.ContainsKey('Remove') ) { $Remove | ForEach-Object { $CallTarget = $(if ( $_ -notmatch '^sip:') { 'sip:' }) + $_ if ( $CallTarget -in $UserCallGroupTargets) { [void]$UserCallGroupTargets.Remove($CallTarget) Write-Verbose -Message "User '$User' - Call Target '$CallTarget' removed to CallGroupTargets" } else { Write-Verbose -Message "User '$User' - Call Target '$CallTarget' is not part of the Call Group" -Verbose } } if ($PSBoundParameters.ContainsKey('Debug') -or $DebugPreference -eq 'Continue') { "Function: $($MyInvocation.MyCommand.Name) - UserCallGroupTargets after REMOVE", ( $UserCallGroupTargets | Format-List | Out-String).Trim() | Write-Debug } $CallGroupOrder = if ( $PSBoundParameters.ContainsKey('TargetInOrder') ) { AvailableCallGroupOrder $User $UserCallGroupTargets.Count } else { $UserCallingSettings.CallGroupOrder } if ($PSCmdlet.ShouldProcess("Calling 'Set-CsUserCallingSettings' to REMOVE CallGroupTargets for '$User'", "$User", 'Set-CsUserCallingSettings')) { try { $CsUserCallingSettings = @{ CallGroupOrder = $CallGroupOrder CallGroupTargets = $UserCallGroupTargets ErrorAction = 'Stop' } $null = Set-CsUserCallingSettings -Identity "$User" @CsUserCallingSettings Write-Information "INFO: User '$User' - CallGroupTargets removed" } catch { $ErrorLog = $_ Write-Error -Message $ErrorLog if ( $WriteErrorLog.IsPresent ) { Write-TFErrorLog -ErrorLog $ErrorLog -Artifact "$User" } } } } #endregion #endregion # Other Settings #region GroupNotificationOverride if ( $PSBoundParameters.ContainsKey('GroupNotificationOverride') ) { if ($PSCmdlet.ShouldProcess("Calling 'Set-CsUserCallingSettings' to set CallGroupTargets for '$User'", "$User", 'Set-CsUserCallingSettings')) { try { $CsUserCallingSettings = @{ GroupNotificationOverride = $GroupNotificationOverride ErrorAction = 'Stop' } $null = Set-CsUserCallingSettings -Identity "$User" @CsUserCallingSettings Write-Information "INFO: User '$User' - GroupNotificationOverride set to $GroupNotificationOverride" } catch { $ErrorLog = $_ Write-Error -Message $ErrorLog if ( $WriteErrorLog.IsPresent ) { Write-TFErrorLog -ErrorLog $ErrorLog -Artifact "$User" } } } } #endregion #region NotificationSetting if ( $PSBoundParameters.ContainsKey('NotificationSetting') ) { #TEST Whether CalLGroupTargets or GroupMemberShip is to be plugged in here - Fairly certain it is the CGT $UserCallingSettings.CallGroupTargets | ForEach-Object { Write-Verbose -Message "User '$User' - Processing CallGroupTarget '$_' - Querying UserCallingSettings" $GMD = (Get-CsUserCallingSettings -Identity $_).GroupMembershipDetails $GMD[[array]::IndexOf($GMD.CallGroupOwnerId, "sip:$User")].NotificationSetting = $NotificationSetting if ($PSCmdlet.ShouldProcess("Calling 'Set-CsUserCallingSettings' to set GroupMembershipDetails for '$_'", "$_", 'Set-CsUserCallingSettings')) { try { $CsUserCallingSettings = @{ GroupMembershipDetails = $GMD ErrorAction = 'Stop' } $null = Set-CsUserCallingSettings -Identity "$User" @CsUserCallingSettings Write-Information "INFO: User '$_' - GroupMembershipDetails for User '$User' - NotificationSetting set to $NotificationSetting" } catch { $ErrorLog = $_ Write-Error -Message $ErrorLog if ( $WriteErrorLog.IsPresent ) { Write-TFErrorLog -ErrorLog $ErrorLog -Artifact "$User" } } } } } #endregion #endregion # OUTPUT $OutputObject = $null if ( $PassThru ) { $OutputObject = Get-TeamsUserCallingSettings -UserPrincipalName $User } Write-Output $OutputObject } } #process end { Write-Verbose -Message "[END ] $($MyInvocation.MyCommand)" } #end } #Set-TeamsUserCallGroup |