Public/Functions/ResourceAccount/New-TeamsResourceAccountAssociation.ps1
# Module: TeamsFunctions # Function: ResourceAccount # Author: David Eberhardt # Updated: 01-OCT-2020 # Status: BETA function New-TeamsResourceAccountAssociation { <# .SYNOPSIS Connects one or more Resource Accounts to a single CallQueue or AutoAttendant .DESCRIPTION Associates one or more existing Resource Accounts to a Call Queue or Auto Attendant Resource Account Type is checked against the ApplicationType. User is prompted if types do not match .PARAMETER UserPrincipalName Required. UPN(s) of the Resource Account(s) to be associated to a Call Queue or AutoAttendant .PARAMETER CallQueue Optional. Specifies the connection to be made to the provided Call Queue Name .PARAMETER AutoAttendant Optional. Specifies the connection to be made to the provided Auto Attendant Name .PARAMETER Force Optional. Suppresses Confirmation dialog if -Confirm is not provided Used to override prompts for alignment of ApplicationTypes. The Resource Account is changed to have the same type as the associated Object (CallQueue or AutoAttendant)! .EXAMPLE New-TeamsResourceAccountAssociation -UserPrincipalName Account1@domain.com - Explanation of what the example does .INPUTS System.String .OUTPUTS System.Object .NOTES Connects multiple Resource Accounts to ONE CallQueue or AutoAttendant The Type of the Resource Account has to corellate to the entity connected. Parameter Force can be used to change the type of RA to align to the entity if possible. .LINK Get-TeamsResourceAccountAssociation New-TeamsResourceAccountAssociation Remove-TeamsResourceAccountAssociation New-TeamsResourceAccount Get-TeamsResourceAccount Set-TeamsResourceAccount Remove-TeamsResourceAccount #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', DefaultParameterSetName = 'CallQueue')] [Alias('New-TeamsRAAssoc')] [OutputType([System.Object])] param( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = "UPN of the Object to change")] [string[]]$UserPrincipalName, [Parameter(Mandatory = $true, ParameterSetName = 'CallQueue', Position = 1, ValueFromPipelineByPropertyName = $true, HelpMessage = "Name of the CallQueue")] [string]$CallQueue, [Parameter(Mandatory = $true, ParameterSetName = 'AutoAttendant', Position = 1, ValueFromPipelineByPropertyName = $true, HelpMessage = "Name of the AutoAttendant")] [string]$AutoAttendant, [Parameter(Mandatory = $false)] [switch]$Force ) #param begin { # Caveat - Script in Development $VerbosePreference = "Continue" $DebugPreference = "Continue" Show-FunctionStatus -Level BETA Write-Verbose -Message "[BEGIN ] $($MyInvocation.MyCommand)" # Asserting AzureAD Connection if (-not (Assert-AzureADConnection)) { break } # Asserting SkypeOnline Connection if (-not (Assert-SkypeOnlineConnection)) { 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') } # Enabling $Confirm to work with $Force if ($Force -and -not $Confirm) { $ConfirmPreference = 'None' } } #begin process { Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand)" # Query $UserPrincipalName [System.Collections.ArrayList]$Accounts = @() foreach ($UPN in $UserPrincipalName) { Write-Verbose -Message "Querying Resource Account '$UPN'" try { $RAObject = Get-AzureADUser -ObjectId $UPN -WarningAction SilentlyContinue -ErrorAction Stop $AppInstance = Get-CsOnlineApplicationInstance $RAObject.ObjectId -WarningAction SilentlyContinue -ErrorAction Stop [void]$Accounts.Add($AppInstance) Write-Verbose "Resource Account found: '$($AppInstance.DisplayName)'" } catch { Write-Error "Resource Account not found: '$UPN'" -Category ObjectNotFound continue } } # Processing found accounts if ($null -ne $Accounts) { #region Connection to Call Queue if ($PSBoundParameters.ContainsKey('CallQueue')) { # Querying Call Queue by Name - need Unique Result Write-Verbose -Message "Querying Call Queue '$CallQueue'" $CallQueueObj = Get-CsCallQueue -NameFilter "$CallQueue" -WarningAction SilentlyContinue if ($null -eq $CallQueueObj) { Write-Error "Call Queue: '$CallQueue' - Not found" -Category ParserError -RecommendedAction "Please check 'CallQueue' exists with this Name" return } elseif ($CallQueueObj.GetType().BaseType.Name -eq "Array") { Write-Verbose -Message "'$CallQueue' - Multiple results found! This script is based on lookup via Name, which requires, for safety reasons, a unique Name to process." -Verbose Write-Verbose -Message "Here are all objects found with the Name. Please use the correct Identity to run New-CsOnlineApplicationInstanceAssociation!" -Verbose $CallQueueObj | Select-Object Identity, Name | Format-Table Write-Error "'$CallQueue' - Multiple Results found! Cannot determine unique result. Please use New-CsOnlineApplicationInstanceAssociation!" -Category ParserError -RecommendedAction "Please use New-CsOnlineApplicationInstanceAssociation!" -ErrorAction Stop } else { Write-Verbose -Message "'$CallQueue' - Unique result found: $($CallQueueObj.Identity)" } # Processing Call Queue Write-Verbose -Message "Processing assignment of all Accounts to Call Queue" foreach ($Account in $Accounts) { # Query existing connection Write-Verbose -Message "'$($Account.UserPrincipalName)' - Querying existing associations" $ExistingConnection = $null #CHECK Query rather with Get-CsOnlineApplicationInstanceAssociation? - Needs ObjectId though! (replicated from Get-TeamsResourceAccountAssociation) #TODO Test Performance of GET-TeamsResourceAccountAssociation VS Get-CsOnlineApplicationInstanceAssociation <# Needs testing $ExistingConnection = Get-CsOnlineApplicationInstanceAssociation (Get-AzureAdUser -ObjectId $Account.UserPrincipalName) -WarningAction SilentlyContinue #> $ExistingConnection = Get-TeamsResourceAccountAssociation $Account.UserPrincipalName -WarningAction SilentlyContinue if ($null -eq $ExistingConnection.AssociatedTo) { Write-Verbose -Message "'$($Account.UserPrincipalName)' - No assignment found. OK" } else { Write-Verbose -Message "'$($Account.UserPrincipalName)' - Existing connections found: Listing all connections. Remove connections or use -Force" -Verbose $ExistingConnection Write-Error -Message "'$($Account.UserPrincipalName)' - This account is already assigned to $($ExistingConnection.ConfigurationType) '$($ExistingConnection.AssociatedTo)'" break } # Comparing ApplicationType if ((Get-TeamsResourceAccount -Identity $Account.UserPrincipalName -WarningAction SilentlyContinue).ApplicationType -eq "CallQueue") { Write-Verbose -Message "'$($Account.UserPrincipalName)' - Application type matches Call Queue - OK" } else { if ($PSBoundParameters.ContainsKey('Force')) { # Changing Application Type Write-Verbose -Message "'$($Account.UserPrincipalName)' - Changing Application Type to 'CallQueue'" -Verbose $null = Set-CsOnlineApplicationInstance -Identity $Account.ObjectId -ApplicationId $(GetAppIdFromApplicationType CallQueue) Start-Sleep -Seconds 2 if ("CallQueue" -ne $(GetApplicationTypeFromAppId (Get-CsOnlineApplicationInstance -Identity $Account.ObjectId -WarningAction SilentlyContinue).ApplicationId)) { Write-Error -Message "'$($Account.UserPrincipalName)' - Application type could not be changed" -Category InvalidType -ErrorAction Stop } else { Write-Verbose -Message "SUCCESS" } } else { Write-Error -Message "'$($Account.UserPrincipalName)' - Application type does not match!" -Category InvalidType -RecommendedAction "Please change manually or use -Force switch" -ErrorAction Stop } } # Establishing Association Write-Verbose -Message "'$($Account.UserPrincipalName)' - Assigning to Call Queue: '$CallQueue'" if ($PSCmdlet.ShouldProcess("$($Account.UserPrincipalName)", "New-CsOnlineApplicationInstanceAssociation")) { $OperationStatus = New-CsOnlineApplicationInstanceAssociation -Identities $Account.ObjectId -ConfigurationType CallQueue -ConfigurationId $CallQueueObj.Identity } } # Re-query Association Target # Wating for AAD to write the Association Target so that it may be queried correctly Write-Verbose -Message "'$Name' Waiting for AAD to write object. Waiting for 2s" Start-Sleep -Seconds 2 $AssociationTarget = Get-CsCallQueue -Identity $OperationStatus.Results.ConfigurationId -WarningAction SilentlyContinue -ErrorAction SilentlyContinue } #endregion #region Connection to Auto Attendant if ($PSBoundParameters.ContainsKey('AutoAttendant')) { # Querying Auto Attendant by Name - need Unique Result Write-Verbose -Message "Querying Auto Attendant '$AutoAttendant'" $AutoAttendantObj = Get-CsAutoAttendant -NameFilter "$AutoAttendant" -WarningAction SilentlyContinue if ($null -eq $AutoAttendantObj) { Write-Error "Auto Attendant: '$AutoAttendant' - Not found" -Category ParserError -RecommendedAction "Please check 'AutoAttendant' exists with this Name" return } elseif ($AutoAttendantObj.GetType().BaseType.Name -eq "Array") { Write-Verbose -Message "'$AutoAttendant' - Multiple results found! This script is based on lookup via Name, which requires, for safety reasons, a unique Name to process." -Verbose Write-Verbose -Message "Here are all objects found with the Name. Please use the correct Identity to run New-CsOnlineApplicationInstanceAssociation!" -Verbose $AutoAttendantObj | Select-Object Identity, Name | Format-Table Write-Error "'$AutoAttendant' - Multiple Results found! Cannot determine unique result. Please use New-CsOnlineApplicationInstanceAssociation!" -Category ParserError -RecommendedAction "Please use New-CsOnlineApplicationInstanceAssociation!" -ErrorAction Stop } else { Write-Verbose -Message "'$AutoAttendant' - Unique result found: $($AutoAttendantObj.Identity)" } # Processing Auto Attendant Write-Verbose -Message "Processing assignment of all Accounts to Auto Attendant" foreach ($Account in $Accounts) { # Query existing connection Write-Verbose -Message "'$($Account.UserPrincipalName)' - Querying existing associations" $ExistingConnection = $null $ExistingConnection = Get-TeamsResourceAccountAssociation $Account.UserPrincipalName -WarningAction SilentlyContinue if ($null -eq $ExistingConnection.AssociatedTo) { Write-Verbose -Message "'$($Account.UserPrincipalName)' - No assignment found. OK" } else { Write-Verbose -Message "'$($Account.UserPrincipalName)' - This account is already assigned to the following entity:" -Verbose $ExistingConnection Write-Error -Message "'$($Account.UserPrincipalName)' - This account cannot be associated as it is already assigned to $($ExistingConnection.ConfigurationType) '$($ExistingConnection.AssociatedTo)'" Continue } # Comparing ApplicationType if ((Get-TeamsResourceAccount -Identity $Account.UserPrincipalName -WarningAction SilentlyContinue).ApplicationType -eq "AutoAttendant") { Write-Verbose -Message "'$($Account.UserPrincipalName)' - Application type matches Auto Attendant - OK" } else { if ($PSBoundParameters.ContainsKey('Force')) { # Changing Application Type Write-Verbose -Message "'$($Account.UserPrincipalName)' - Changing Application Type to 'AutoAttendant'" -Verbose $null = Set-CsOnlineApplicationInstance -Identity $Account.ObjectId -ApplicationId $(GetAppIdFromApplicationType AutoAttendant) Start-Sleep -Seconds 2 if ("AutoAttendant" -ne $(GetApplicationTypeFromAppId (Get-CsOnlineApplicationInstance -Identity $Account.ObjectId -WarningAction SilentlyContinue).ApplicationId)) { Write-Error -Message "'$($Account.UserPrincipalName)' - Application type could not be changed" -Category InvalidType -ErrorAction Stop } else { Write-Verbose -Message "SUCCESS" } } else { Write-Error -Message "'$($Account.UserPrincipalName)' - Application type does not match!" -Category InvalidType -RecommendedAction "Please change manually or use -Force switch" } } # Establishing Association Write-Verbose -Message "'$($Account.UserPrincipalName)' - Assigning to Auto Attendant: '$AutoAttendant'" if ($PSCmdlet.ShouldProcess("$($Account.UserPrincipalName)", "New-CsOnlineApplicationInstanceAssociation")) { $OperationStatus = New-CsOnlineApplicationInstanceAssociation -Identities $Account.ObjectId -ConfigurationType AutoAttendant -ConfigurationId $AutoAttendantObj.Identity } } # Re-query Association Target # Wating for AAD to write the Association Target so that it may be queried correctly Write-Verbose -Message "'$Name' Waiting for AAD to write object. Waiting for 2s" Start-Sleep -Seconds 2 $AssociationTarget = Get-CsAutoAttendant -Identity $OperationStatus.Results.ConfigurationId -WarningAction SilentlyContinue -ErrorAction SilentlyContinue } #endregion #region Output $ResourceAccountAssociationObject = $null $ResourceAccountAssociationObject = [PSCustomObject][ordered]@{ UserPrincipalName = $Accounts.UserPrincipalName ConfigurationType = $OperationStatus.Results.ConfigurationType Result = $OperationStatus.Results.Result StatusCode = $OperationStatus.Results.StatusCode StatusMessage = $OperationStatus.Results.Message AssociatedTo = $AssociationTarget.Name } Write-Output $ResourceAccountAssociationObject #endregion } else { Write-Warning -Message "No Accounts found" } } #process end { Write-Verbose -Message "[END ] $($MyInvocation.MyCommand)" } #end } #New-TeamsResourceAccountAssociation |