Teams Rufnummern

Wer mit Teams telefoniert, braucht nicht nur einen "Amtsanschluss über Microsoft, Operator Connect oder Direct Routing, sondern auch die passende Lizenz und Konfiguration. Microsoft braut im Unterbau kontinuierlich etwas um und manchmal erhascht man einen Blick hinter die Kulissen.

Felder und Zusammenhänge

Wenn wir beim lokalen Active Directory starten, dann hat jeder Benutzer gleich mehrere AD-Felder, in denen Rufnummern stehen können. Hier ein Ausschnitt:

Dahinter verbergen sich natürlich unterschiedliche Felder, von denen einige Mehrfachfelder sind wie:

  • telephoneNumber
  • telephoneAssistant
  • telexNumber
  • facsimileTelephoneNumber
  • ipPhone
  • homePhone
  • mobil
  • otherFacsimileTelephoneNumber
  • otherHomePhone
  • otherIpPhone
  • otherMobile
  • otherTelefone

Einige der Fehler sind sogar mehrdeutig aber alle haben gemeinsam, dass Sie weder für Skype for Business noch für Teams direkt relevant sind. Natürlich werden die Felder mit ADSync auch in die Cloud repliziert, damit Sie bei der "Contact Card" eines Benutzers die ganzen Rufnummern sehen und sogar anklicken können. Sie haben aber keine Funktion für das Routen von Anrufen.

  • SFB-OnPrem: msRTCSIP-Line
    Der Inhalt dieses Feldes bestimmt, unter welcher Rufnummer der Anwender aus der Skype for Business-Welt erreichbar ist und welche ausgehende rufende Nummer angezeigt wird.
  • Online: OnPremLinUri
    In der Cloud gibt es (Stand Jun 2022) immer noch das Feld "OnPremLinUri" und es wird auch vom ADSync verwaltet. Skype for Business Online hat das Feld genutzt und Anfang sogar Teams.
  • Online: LineURI
    Mit der Abkündigung von "OnPremLinUri" gewinnt das Feld an Bedeutung, da es in der Cloud die maßgebliche Rufnummer zum Routen von Telefonien-Anrufen ist.

Vielleicht gibt es noch einige weitere Nummern, aber nur die letzten drei Felder sind für die weitere Betrachtung relevant. Wobei auch Microsoft hier schreibt:

In Teams PowerShell Module version 3.0.0 and later, the OnPremLineURI attribute refers only to the LineURI that's set via OnPrem AD. Previously, OnPremLineURI also referred to Direct Routing numbers that were assigned to users via the Set-CsUser cmdlet. OnPremLineUriManuallySet is now deprecated as OnPremLineURI is representative of the On-Prem assignment. Also, Direct Routing numbers are available in the LineURI attribute. You can distinguish Direct Routing Numbers from Calling Plan Numbers by looking at the FeatureTypes attribute.
Quelle: Gte-CSOnlineUser https://docs.microsoft.com/en-us/powershell/module/skype/get-csonlineuser?view=skype-ps#parameters

Damit wissen wir etwas mehr um die Zusammenhänge aber auch das baldige Ende von Set-CSUser für Rufnummern. Am 10.6.2022 konnte ich meinen Benutzer nur noch mit einer Warnung konfigurieren:

Ich kann zwar ein "Set-CSUser -OnPremLineURI" machen, aber das Feld bleibt leer. Es wird nur durch ADSync geschrieben.

SfB to Teams

Ich fange damit an, dass ich einen Benutzer in Skype for Business OnPremises mit Enterprise Voice aktiviert und einer Rufnummer versehen habe. Dieser Benutzer konnte telefonieren und angerufen werden. Maßgeblich war neben dem SfB-Homeserver natürlich auch der Dialplan, die Voice-Routen  mit PSTN-Usages und ein geeigneter SIP-Trunk.

Mit dem Wechsel in die Cloud Richtung Teams wurde ADSync und SfB Hybrid-Mode eingerichtet und dann der Benutzer in die Cloud verschoben. Im Hintergrund hat aber ADSync schon diese Informationen in die Cloud repliziert. Auch dazu habe ich auf ADSync mit Skype für Business schon einiges geschrieben. Daher fasse ich mich hier kurz und zeige nur, was ADSync für meinen fctest1 in die Cloud schreibt.

Wenn ich mit den Benutzer in der Cloud angeschaut habe, war der Benutzer schon dort angelegt aber noch etwas uneinheitlich.

PS C:\> get-csonlineuser fctest1 | fl *enab*,*lineuri,*time*,*dirsync*

AccountEnabled                   : True
EnterpriseVoiceEnabled           : False
IsSipEnabled                     : False
OnPremEnterpriseVoiceEnabled     : True
OnPremSIPEnabled                 : True
TeamsUpgradeNotificationsEnabled : False
UserDirSyncEnabled               : True
LineUri                          :
OnPremLineUri                    : tel:+495257123
LastSyncTimeStamp                : 08.06.2022 11:33:41
SoftDeletionTimeStamp            :
LastProvisionTimeStamps          : {[UserAuthoredProps, 2022-03-27T22:03:35.4212489+00:00]}
LastPublishTimeStamps            : {[ProvisionedPlanPublishAuthoredProps, 2022-03-27T21:48:36.0708285+00:00]}
UserDirSyncEnabled               : True

Das ist bei mir gewollt, da der Benutzer in der Cloud noch keine Lizenz hatte. Es ist aber gut zu sehen, dass die OnPremLineUri gefüllt ist aber die LineURI nicht. Sie sehen aber auch, dass "OnPremEnterpriseVoiceEnabled:$true" aber "EnterpriseVoiceEnabled:$false" ist. Der Werte "EnterpriseVoiceEnabled" wird wohl nicht durch ADSync abgeglichen. Das ist aber auch kein Wunder, wenn ein Aktivieren funktioniert mangels Lizenz nicht.

PS C:\> Set-CsPhoneNumberAssignment -Identity fctest1@uclabor.de -EnterpriseVoiceEnabled $true
Set-CsPhoneNumberAssignment_Set: C:\Users\adm-fcarius\Documents\PowerShell\Modules\MicrosoftTeams\4.4.1\custom\PsExt\Merged_custom_PsExt.ps1:5935
Line |
5935 |          Microsoft.Teams.ConfigAPI.Cmdlets.internal\Set-CsPhoneNumberA …
     |          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | The user 'db4892f9-dbc1-4a2d-bdb2-48fc04c7b4ff' does not have required licenses to set attributes

Erst nachdem ich die Lizenz zugewiesen und etwas gewartet habe, konnte ich den Benutzer für "EnterpriseVoiceEnabled:$true" in der Cloud aktivieren. Interessanterweise wird durch den Prozess das Feld "LineUri" gefüllt.

PS C:\> get-csonlineuser fctest1 | fl *enab*,*lineuri,*time*,*dirsync*

AccountEnabled                   : True
EnterpriseVoiceEnabled           : True
IsSipEnabled                     : True
OnPremEnterpriseVoiceEnabled     : True
OnPremSIPEnabled                 : True
TeamsUpgradeNotificationsEnabled : False
UserDirSyncEnabled               : True
LineUri                          : tel:+495257123
OnPremLineUri                    : tel:+495257123
LastSyncTimeStamp                : 08.06.2022 14:45:51
SoftDeletionTimeStamp            :
LastProvisionTimeStamps          : {[UserAuthoredProps, 2022-06-08T14:45:17.3444633+00:00]}
LastPublishTimeStamps            : {[ProvisionedPlanPublishAuthoredProps, 2022-06-08T14:25:24.2197851+00:00]}
UserDirSyncEnabled               : True

Der Versuch nun diesen Wert manuell zu überschreiben, funktioniert nicht:

PS C:\> Set-CsPhoneNumberAssignment -Identity fctest1@uclabor.de -PhoneNumber "+495257456" -PhoneNumberType DirectRouting

Set-CsPhoneNumberAssignment_Set: C:\Users\adm-fcarius\Documents\PowerShell\Modules\MicrosoftTeams\4.4.1\custom\PsExt\Merged_custom_PsExt.ps1:5935
Line |
5935 |          Microsoft.Teams.ConfigAPI.Cmdlets.internal\Set-CsPhoneNumberA …
     |          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | The user 'db4892f9-dbc1-4a2d-bdb2-48fc04c7b4ff' already has a telephone number set in the on-premises
     | Active Directory and synchronized to the cloud. To manage this user's telephone number in the cloud,
     | first clear the phone number in the on-premises directory and synchronize to Active Directory.

Als Flussdiagramm sieht das dann so aus:

Nun möchte ich natürlich die Konfiguration allein in der Cloud machen. Dazu muss ich das Feld "msRTCSIP-Line" im lokalen AD leeren. Das ist der von Microsoft empfohlene Weg und wird beschrieben:

Allerdings sollten Sie den Artikel komplett lesen, denn es ist mehr als nur ein Feld zu leeren.

Diese Änderung bewirkt auch auch ein "Wipe" beider Felder in der Cloud.

In der Folge kann der Anwender nicht mehr telefonieren oder angerufen werden, bis Sie den Fehler "korrigiert" haben.

PS C:\> get-csonlineuser fctest1 | fl *enab*,*lineuri,*time*,*dirsync*

AccountEnabled                   : True
EnterpriseVoiceEnabled           : True
IsSipEnabled                     : True
OnPremEnterpriseVoiceEnabled     : False
OnPremSIPEnabled                 : True
TeamsUpgradeNotificationsEnabled : False
UserDirSyncEnabled               : True
LineUri                          :
OnPremLineUri                    :
LastSyncTimeStamp                : 08.06.2022 17:57:36
SoftDeletionTimeStamp            :
LastProvisionTimeStamps          : {[UserAuthoredProps, 2022-06-08T17:56:54.9569443+00:00]}
LastPublishTimeStamps            : {[ProvisionedPlanPublishAuthoredProps, 2022-06-08T14:25:24.2197851+00:00]}
UserDirSyncEnabled               : True

Sie müssen hier also nach dem Update durch ADSync zügig die LineURI nachpflegen, was nun aber geht, da das Feld nun "leer" ist.

PS C:\> Set-CsPhoneNumberAssignment -Identity fctest1@uclabor.de -PhoneNumber "+495257456" -PhoneNumberType directrouting
PS C:\> Get-CSOnlineUser fctest1 | fl *enab*,*lineuri,*time*,*dirsync*

AccountEnabled                   : True
EnterpriseVoiceEnabled           : True
IsSipEnabled                     : True
OnPremEnterpriseVoiceEnabled     : False
OnPremSIPEnabled                 : True
TeamsUpgradeNotificationsEnabled : False
UserDirSyncEnabled               : True
LineUri                          : tel:+495257456
OnPremLineUri                    :
LastSyncTimeStamp                : 08.06.2022 17:57:36
SoftDeletionTimeStamp            :
LastProvisionTimeStamps          : {[UserAuthoredProps, 2022-06-08T18:09:46.4478576+00:00]}
LastPublishTimeStamps            : {[ProvisionedPlanPublishAuthoredProps, 2022-06-08T14:25:24.2197851+00:00]}
UserDirSyncEnabled               : True

ADSync blockiert also nicht das Property generell, sondern Teams verhindert das Schreiben der LineUri, solange in OnPremLineUri etwas steht.

In der Teams PowerShell habe ich eine Erläuterung zu den Hintergrunden gefunden:

In Teams PowerShell Module version 3.0.0 and later, the OnPremLineURI attribute refers only to the LineURI that's set via OnPrem AD. Previously, OnPremLineURI also referred to Direct Routing numbers that were assigned to users via the Set-CsUser cmdlet. OnPremLineUriManuallySet is now deprecated as OnPremLineURI is representative of the On-Prem assignment. Also, Direct Routing numbers are available in the LineURI attribute. You can distinguish Direct Routing Numbers from Calling Plan Numbers by looking at the FeatureTypes attribute.
Quelle: https://docs.microsoft.com/en-us/powershell/module/skype/get-csonlineuser

Migration in die Cloud

Das bedeutet nun natürlich erhöhte Aufmerksamkeit bei der Umstellung von Teams Telefonie-Benutzern in die Cloud beim Rückbau des lokalen Skype for Business Topologie mit abschließenden Disable-CSUser für alle AD-Objekte zum Rückbau der SfB Topology.

Die Herausforderung besteht nun darin, den Export der Daten, den Disable-CSUser, ADSync und die Nachpflege in Teams möglichst kurz zu halten.

Microsoft beschreibt das auf der folgenden Seite sehr gut, dass Sie entweder das Feld "msRTCIP-Line" weiterhin OnPremises pflegen und per ADSync in die Cloud replizieren oder sie räumen OnPremises die Einträge weg und reparieren Sie in der Cloud.

Speziell in größeren Umgebungen Vor dem Cleanup sollten Sie aber erst einmal ihren beiden Systemen, AD und Online, auf die Finger schauen, was Sie denn schon alles wissen und die Konsistent das alles ist.

Damit sollten Sie einen Überblick bekommen, welche Objekte vielleicht schon vorher "korrupt" sind oder noch nicht die passende Lizenz für Teams Telefonie haben. Microsoft beschreibt selbst, wie man die Rufnummer von dem OnPremises Server bezieht. Wenn Sie aber heute schon ADSync nutzen, dann wird der Inhalt von "msRTCSIP-Line" in "OnPremLineURI" repliziert, was aber aber in der Teams PowerShell 4.4.0 nicht auslesbar ist ("Not yet supported", siehe Get-CSOnlineUser). Aber die gleiche Nummer steht auch im Feld "LineURI", welches ausgelesen werden kann. Daher ist es aus meiner Sicht auch möglich indirekt die Rufnummern aller Personen zu exportieren.

get-csonlineuser `
| select sipaddress,enterprisevoiceenabled,lineuri,onpremlineuri,`
         dialplan,TenantDialPlan,TeamsUpgradeEffectiveMode,`
         @{name="EV";expression={$_.AssignedPlan.Capability.contains("MCOEV")}} `
| export-csv cstelefonie.csv -notypeinformation

Prüfen Sie, dass alle Benutzer mit Telefonie auch auf "EnterpriseVoiceEnabked:$true" stehen, einen Dialplan haben und TeamsOnly als TeamsUpgradeEffectiveMode nutzen und eine Phone System-Lizenz (MCOEV) haben.

Nach der "OnPremises"-Umstellung mit dem finalen "Disabled-CSUser" im lokalen AD sind natürlich die Rufnummern entfernt. Dann ist es schon sehr einfach, diese Werte wieder anzuwenden.

Import-CSV cstelefonie.csv | foreach { `
   Set-CsPhoneNumberAssignment `
      -Identity $_.sipaddress `
      -PhoneNumber $_.lineuri.Replace("tel:","") `
      -PhoneNumberType Directrouting
}

Hinweis:
Sie sollten die Rufnummer mit "Set-CsPhoneNumberAssignment" setzen. Früher war "Set-CSUser" ebenfalls möglich aber ist seit ca. Jun 2022 nicht mehr möglich. Set-CsPhoneNumberAssignment braucht aber deutlich länger. Ich weiss nicht, ob es nur an der API liegt oder das Commandlet auf eine Bestätigung wartet.

Schreibschutz

Ich habe natürlich ausprobiert, was nach der Umstellung passiert Ich habe den Benutzer mit Teams Enterprise Voice versehen und das AD-Feld "msRTCSIP-Line" war leer. Ich habe dann "durch einen Fehler" das Feld im lokalen AD gefüllt. Beim nächsten ADSync-Lauf wurde die Rufnummer aus dem lokalen AD wieder in das Feld "OnPremLineUri" in der Cloud geschrieben UND in das Feld "LineUri" in der Cloud übertragen. Eine manuell dort konfigurierte abweichende Rufnummer wurde ohne Rückfrage überschrieben!

Wenn dann im lokalen AD die Rufnummer wieder gelöscht wurde, hatte der Anwender in Teams gar keine Rufnummer mehr., d.h. Teams hat nicht den alten Zustand wieder hergestellt.

Ich finde die "Lösung", die Microsoft hier vorgibt, sehr unglücklich. Wenn ich mit OnPremLineUri und LineUri schon zwei Felder in der Cloud habe, dann sollte ich diese auch unabhängig voneinander nutzen können, d.h. dass die LineURI die OnPremLineURI einfach überstimmt, wenn Sie manuell gesetzt wird.

Achtung:
Aus meiner Sicht ist es höchst gefährlich, die Replikation der msRTC-Felder mit ADSync aktiv zu lassen, wenn Sie lokal keinen SfB Server mehr betreiben aber vielleicht alte Provisioning-Skripte weiter in das AD schreiben.

Daher rate ich dazu, im ADSync die Replikation dieser msRTC-Felder zu deaktivieren. Anstatt nun aber im "Regel-Editor" etwas zu ändern, sollten Sie die ADSync Konfiguration über die "Azure AD app and attribute filtering" anpassen. Hier können Sie komplette Apps oder auch nur einzelne Felder ausschließen:

Diese Einstellung sorgt dann dafür, dass die entsprechenden "Outbound"-Regeln in den Synchronisierungsregeln entfernt werden:

Achtung
Durch diese Änderunge schreibt und liest ADSync die Felder nicht mehr. Das bedeutet aber auch, dass Sie in der Cloud z.B. die "OnPremLineURI" damit nicht mehr löschen können!

Diese Änderung sollten Sie erst aktivieren, nachdem wirklich alle jemand von ADSync in die Cloud geschriebenen Felder in der Cloud auch gelöscht wurde

Schade, dass ADSync hier zumindest beim "FullSync" die früher einmal vorgenommen Änderungen nicht mehr rückgängig macht.

Weitere Links