Migrating from Microsoft.bot.teams preview to Microsoft.bot.builder 4.6

Let’s say, for example, that you were building a Teams bot over the summer, and wanted to use all of the latest bot framework 4 stuff.  There were a lot of beta/prerelease versions out there, that have since all been cleaned up into Microsoft.bot.builder 4.6.  The good news is that this should simplify everything, but the bad news is that there are a couple of (as yet undocumented) breaking changes in the update. 

Once you remove the Microsoft.Bot.Builder.Teams package and add Microsoft.Bot.Builder (4.6.3 as of today), you’ll probably get a couple of build errors.  Here’s what I found and how I fixed them:

  • In ConfigureServices, there was a call to services.AddBot<T>, and one of the required options was adding a new TeamsMiddleware object.  This doesn’t seem to be required anymore, so just take it out.
  • The ITeamsContext doesn’t seem to exist anymore, which you were able to get on a dialog turn with “var teamsContext = turnContext.TurnState.Get<ITeamsContext>();”.  I was mainly using this to get the channel ID of a message in a dialog turn, which now appears to be embedded in the channelData as JSON.  There doesn’t appear to be a method to serialize this, but you can just extract it from the json manually like this:

private static string GetChannelID(object channelData)
         //if the channelData contains a channel ID, return it, otherwise null
         if (!(channelData is JObject))
             return null;
         JObject j = (JObject)channelData;
         if (j.TryGetValue(“channel”, out var channel))
             return channel[“id”].ToString();
         return null;
     catch (Exception ex)
         _log.Error($”Exception parsing channel data {channelData} “, ex);       
     return null;

  • The constructors for some responses have changed, so where before you’d have something like:
  • var resp = new TaskModuleContinueResponse(“continue”);

    you now have:

    var resp = new TaskModuleContinueResponse();
    resp.Type = “continue”;

  • The ToAttachment extension on adaptive cards seems to have been taken out, which was handy for dynamically defining cards for task modules.  Fortunately, the source is still on github, so it’s pretty easy to grab it and define your own copy of that method if you’re using it already:

public static partial class CardExtensions
     public static Attachment ToAttachment(this AdaptiveCards.AdaptiveCard card)
         return new Attachment
             Content = card,
             ContentType = AdaptiveCards.AdaptiveCard.ContentType,

That’s about all I found that was broken, and otherwise everything seemed to work in the bot after the update.  There might be other things in the SDK now that would be better practice, but I’ll have to dig into that a little more later.  For now, I can at least get all the prerelease stuff out of the project. 

It’s great to see Teams as a first class bot citizen now-looking forward to what’s next!

    Posted in Uncategorized | Leave a comment

    Beware onprem DLs-they could delete a cloud group (including Teams)

    I’ve run into this a couple of times, and fortunately now there’s a way to fix it.  This is a case of Azure AD sync being a little…aggressive with syncing onprem objects to the cloud.  Say you’d created a Team, which creates an O365 group, with the address team1@domain.com.  You have some team channels, notebooks, and everything is working as expected.  Then, because you have a hybrid mail setup, an exchange admin creates a distribution list onprem (because that’s the way they’ve always done things) called… team1@domain.com.  You would think that this would just cause a sync error the next time it goes to sync online, but nope-the onprem object wins, and the Team, Group, and all associated content is deleted in favour of the “dumb” DL. 

    The first time this happened, there was no way to recover.  Any messages that had been sent to the modern group were lost, and we had to start from scratch.  The second time this happened was last week, and fortunately there’s now a way to recover a deleted group.  In this case, I just had to delete the onprem DL, restore the cloud group, and all was right with the world again.  You have 30 days until the “soft delete” expires, so as long as you catch it reasonably quickly, things will be recoverable. 

    Posted in Uncategorized | Leave a comment

    Moving from SFB to Teams-Contacts from SFB not importing, even when contact store is set to SFB

    When I started moving users to TeamsOnly, one of the first pilot users said that their contacts didn’t import from SFB.  Some digging around turned up this blog post, that outlined how to roll back the Unified Contact Store (UCS) to SFB server to allow contacts to import.  The challenge here though, is that the user’s account still listed SFB server as the contact store location. 

    Figuring it couldn’t hurt, I ran Invoke-CsUcsRollback for the user ID, and it forced the import on the next sign in.  Given that UCS isn’t supported for TeamsOnly anyway, this has become part of the standard command set that I’ve run to move users from SFB to TeamsOnly. 

    Posted in Uncategorized | Leave a comment

    Moving from SFB online to Teams-fix outbound calling appearing for TeamsOnly users

    This was another gotcha as part of the move from SFB online to Teams, and it only happened once (so far), but it’s another reason why moving users via Islands might be the way to go.  This was the scenario: a user was in SfbWithTeamsCollab, and moved to TeamsOnly.  After a couple of days in this scenario (just to make sure that it wasn’t timing-related), they could chat, and they could receive calls, but not make any calls.  Again, we tried the usual signing in via an incognito browser, cache clear, sign out and back in, reboot, and the other standard troubleshooting steps, but despite being enabled for everything they should be enabled for, they couldn’t make outbound PSTN calls.

    The end result was really the server-side equivalent of rebooting, which was to run:

    • Set-CsUser $id –EnterpriseVoiceEnabled $false
    • wait 10 minutes…
    • Set-CsUser $id –EnterpriseVoiceEnabled $true

    Which magically fixed things.  Having to wait for replication (and not being able to check replication status like in SFB) takes some patience, but eventually calling started working as expected. 

    Posted in Uncategorized | Leave a comment

    Moving from SFB to Teams-Why your guest users can’t chat, and how to fix it

    This is a reasonably specific case, but I thought it was kind of interesting.  When we first started piloting Teams we were in Islands, just like everyone else.  At that point people set up Teams, some of which had guest users, and everything worked just fine.  Then, as part of the migration, I switched the Org-Wide default to SfbWithTeamsCollab, and then started moving users over to TeamsOnly.  One of the consequences of this is that none of the guest users could chat with our internal users anymore, even those that were converted to TeamsOnly.

    As it turns out, the Org-Wide upgrade mode dictates whether guest users can chat inside a guest tenant.  Sure, there’s a setting in the admin center (Guest Access, Messaging, Chat), but if the tenant default is SfbWithTeamsCollab, it doesn’t have any effect. 

    To get out of this situation, I needed to change the default, but without changing the state of any of the users that hadn’t been migrated yet.  Of course, you can’t just run Grant-CsTeamsUpgradePolicy with SfbWithTeamsCollab, since Teams will stop you from doing something redundant.  The way around this though, is to note that there are actually two policies:


    Note that one has the notification flag, and one doesn’t, so I could just iterate through the users with a $null policy assigned, and manually grant SfbWithTeamsCollabWithNotify to them.  Once that was done, I changed the global default, accepted the scary warning that we’d now be TeamsOnly, and fixed guest access chat.

    Again, I have no idea whether this is going to be a problem forever, and it smells like a bug to me, but at least in September 2019, this is still the way things behave.  Fortunately, there’s a way around it, and it also has the benefit of letting you move users to Teams by just removing a per-user policy instead of assigning one.  Might have been nice to know this BEFORE starting to move users, but that’s why I’m publishing this.  If it helps someone avoid the same issue (or at least fix it)-mission accomplished!

    Posted in Uncategorized | Leave a comment

    Moving from SFB to Teams-If you’re in SFB* consider visiting the Island on the way…

    Given the imminent demise of SFB online, I figured it was time to start moving more users over to TeamsOnly.  As with most O365 orgs, we’d been in Islands for a while, but to start moving anyone to Teams, you’re going to want to choose an interop mode than relegates chat/calling to one client, otherwise TeamsOnly users will only be able to chat with other users on Teams, which is less than transparent.  For us, this meant changing the org-wide default to SFBWithTeamsCollab, and then granting TeamsOnly to users as they got migrated.  This seemed like a reasonably straightforward approach, and a few days after having changed the default, I moved a batch of users to TeamsOnly one evening, thinking that the next morning things would have moved correctly…

    As it turns out, 2/3 of the pilot group were still unable to chat or make calls in Teams, even after sign outs, reboots, cache clears, and incognito chrome sessions.  What’s worse, when they signed out of SFB, they were informed that they’d been upgraded to Teams, so effectively they were unreachable in either platform.  The solution, in this case, was to move the users BACK to islands, wait a few hours, and then move them back to TeamsOnly once the provisioning had kicked in to enable calling and chat again (I assigned a direct routing voice policy while in Islands too, which isn’t technically supported, but works nonetheless).  This seemed to solve the problem.

    So, based on this lesson, my new move technique for getting a user on Teams is:

    • Grant-CsTeamsUpgradePolicy $id -PolicyName Islands
    • Grant-CsOnlineVoiceRoutingPolicy $id -PolicyName Unrestricted
    • wait 24h
    • Grant-CsTeamsUpgradePolicy $id -PolicyName UpgradeToTeams
    • Invoke-CsUcsRollback $id

    By putting the user in Islands for 24h, it makes sure that the chat/calling are enabled correctly before changing how things are routed.  I’ve done this for a couple of other batches over a weekend (Islands Friday night, TeamsOnly Saturday night), and it’s gone much smoother than SfbWithTeamsCollab straight to TeamsOnly.  This might not be a requirement forever, but as of now (September 2019), it certainly helps.

    Posted in Uncategorized | Leave a comment

    Issues moving users back to on-prem telephony from an expired calling plan license

    This might be a unique situation, but I wanted to document it somewhere in case anyone else runs into the same issues.  Here’s the scenario: I have an O365 tenant set up with hybrid voice (SFB 2015 Enterprise on prem), with users homed online.  User line URIs are set on prem, replicated to the cloud, and PSTN calls in/out go via the onprem server.  This is a textbook hybrid deployment, and everything was working just fine.

    A few months ago, calling plans came to Canada, and we got involved in the preview, so I assigned licenses to a couple of accounts to test it out.  The online phone numbers worked just fine, and I promptly forgot all about them until the licenses expired, at which point a user with an online phone number that tried to make a call got connected to an announcement service saying that “you are not configured for this calling feature”.  Obviously, at this point I wanted to revert those users back to the onprem connectivity.

    Step 1-try removing the phone number from the user in the portal…doesn’t work.  It just informs you that “phone numbers could not be unassigned from 1 of 1 users”.  OK, then I thought maybe I had to remove the calling plan license from the user first.  Tried that, but then still couldn’t unassign the phone number.  I also tried going in and removing the phone number from the phone numbers interface, which gave me the same error.  At this point I decided that powershell might be the way to force the issue, and was able to run Remove-CsOnlineTelephoneNumber to delete the number from the account. 

    At this point however, I had a user that had an OnPremLineUri assigned, but no LineUri.  EnterpriseVoiceEnabled was still set to $true (get-csonlineuser), but the user’s PSTN connectivity still showed as Online in the portal, with no apparent way to reset it.  I tried a few different things at this point, with no success:

    • Run a new AD sync: no effect
    • Disable/re-enable EV, thinking it might force it to pick up the setting again: no effect
    • Move the user back on prem, and then online: This actually worked, in that while the user was homed onprem, they could make and receive calls, but when I moved them back online, the original problem returned. 

    At this point, I gave up and opened a support ticket, and the best I managed to get from the Engineer there was “wait 24 hours”…

    The next day, I checked the account, and lo and behold, the user’s line URI had reverted.  I had to re-enable EV for the user, but once I did (and waited for the provisioning to take effect), everything was back to normal. 

    So, lessons learned:

    • It’s probably a good idea not to let calling plan licenses expire.
    • If you do have to move a user from calling plan back to onprem PSTN, it might take a while. 
    • Once you do move them, you might have to re-enable EV across the board.
    • This might get messy at scale, with users unable to make/receive calls for a period of time.

    If anyone has figured out a way to get this to take effect more immediately, I’d love to know about it, but in the meantime, if you have the same issue, I suppose the only solution is to just wait it out.

    Posted in Uncategorized | Leave a comment