After some early challenges, Microsoft has significantly improved its developer tooling for creating custom Microsoft Teams applications in recent years. This effort centers on the Teams Toolkit (TTK) label. While many developers think of TTK as just the VS Code extension, it encompasses much more!
In addition to the VS Code extension, the toolkit includes the TTK CLI (aka: teamsapp), a comprehensive suite of utility packages with React hooks for Teams apps, and numerous other tools. Many of these were previously known under the TeamsFx label. You can find these tools, libraries, and their source code in the github.com/OfficeDev/teams-toolkit repository.
However, TTK VS Code extension & CLi hav one significant limitation: they don’t work in non-commercial clouds.
According to the Teams Toolkit for VS Code documentation, it lacks support for GCC, GCC High, DoD, and other government clouds.
Microsoft Teams Toolkit Overview - Not supported in GCC, GCC High, or DoD environments.
It’s not just TTK - the Microsoft Teams Developer Portal is not fully supported in these same environments, further making Teams app development challenging for many customers in the public sector. Microsoft started some work on it, announcing a beta Developer Portal in October 2022, but there hasn’t been much since then.
Recently I’ve been working with some of my coaching clients who are in GCC environments and I’ve seen the pain and frustration this causes, especially since the Microsoft documentation doesn’t address this scenario. In this article, I want to share a process we’ve come up that leverages the flexibility of the TTK to make the process work… albeit with some tedious manual tasks.
Overview - Customizing the Build Process
The biggest challenge you face with the TTK for VS Code (VSC), and the associated CLI, is most operations require you are logged into your Microsoft 365 tenant where you want to test your app. For customers in GCC environments, I find most of them don’t just deploy to to GCC environments but they also develop in them as well. This is required because of the nature of the organization in some cases, while in others have it as a policy to ensure development is as close to production as possible.
To get around these blockers but still get most of the productivity enhancements from the TTK for VSC, we leveraged the flexibility in TTK based projects. There are some manual steps you have to do to each project, but this process works great from my experience.
I’m interested in hearing your experience with this process - please leave a comment at the end of the article!
This process involves making changes to the tasks and launch configurations in TTK-based projects and a few edits to the provision stage in the local development project file. The biggest thing you have to do is remove the dependency by being logged in and connected to a Microsoft 365 tenant.
Once you’ve made these changes, you’ll then manually upload the Teams app package once you’ve signed into Teams in the browser VSC will launch and attach to for debugging.
If your application uses single sign-on (SSO), you’ll need to manually create and configure the Entra ID application used in the SSO configuration.
Let’s get started!
Update the VS Code Tasks
Start by making a few changes to the VSC tasks defined in the ./.vscode/tasks.json file in the project. Ideally I’d like to duplicate and edit two of the default tasks for our use, but the TTK will first ensure you’re logged into your Microsoft 365 account: validating prerequisites & provisioning resources. The point of these steps is to get around the scenario where we can’t login to our Microsoft 365 account, so we can’t use those.
That means you’ll have to manually perform some of the steps in those tasks.
While you can modify the existing tasks, I’m going to create new tasks so the project will still work with regular non-GCC Microsoft 365 tenants.
Create the initial “start” task
Next, duplicate the start task that you’ll normally run if you weren’t in a GCC tenant:
{
"label": "Start Teams App Locally",
"dependsOn": [
"Validate prerequisites",
"Provision",
"Deploy",
"Start application"
],
"dependsOrder": "sequence"
}
In the new copy, do the following:
- Remove the
Validate prerequisites
task from we can’t use from thedependsOn
array. This triggers the TTK to force you to login to your Microsoft 365 tenant, which we can’t do in GCC environments. - Rename the
label
property toStart Teams App Locally (for GCC)
.
The new task should look like the following:
{
"label": "Start Teams App Locally (for GCC)",
"dependsOn": [
"Provision",
"Deploy",
"Start application"
],
"dependsOrder": "sequence"
}
Update the VS Code Launch configurations
While you can run tasks manually in VSCode using the Tasks: Run Task
command from the command bar, most prefer to use the selector in the Run and Debug panel to get additional debugging configurations. These relay on the tasks defined in the file you previously modified.
Create a new config to launch the Teams web client
What we want to do is add a new entry to this list, like you see in the screenshot above: Debug in Teams for GCC (Edge). The easy to way to start is by duplicating an existing entries, so within the configurations
array, locate the configuration named Attach to Frontend in Teams (Edge)
and duplicate it:
{
"name": "Attach to Frontend in Teams (Edge)",
"type": "msedge",
"request": "launch",
"url": "https://teams.microsoft.com/l/app/${{local:TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&${account-hint}",
"presentation": {
"group": "all",
"hidden": true
},
"cascadeTerminateToConfigurations": [
"Attach to Local Service"
],
"internalConsoleOptions": "neverOpen"
},
In the new copy, rename it to set the name
to Attach to Frontend in Teams for GCC (Edge)
and change the url
property to only include the base URL for the Teams web client. The default URL will tell Teams to start installing an app when the client loads - we don’t want this because we’ll have to manually upload the Teams app package since the TTK can’t do this for us as it can’t connect to Microsoft 365 GCC tenants:
{
"name": "Attach to Frontend in Teams for GCC (Edge)",
"type": "msedge",
"request": "launch",
"url": "https://teams.microsoft.com",
"presentation": {
"group": "all",
"hidden": true
},
"cascadeTerminateToConfigurations": [
"Attach to Local Service"
],
"internalConsoleOptions": "neverOpen"
}
Create a new config to attach to the web & local server side processes
Next, we need to add a new compound launch configuration that will do a few things. When triggered, it will:
- Run new task
Start Teams App Locally (for GCC)
we created to bypass the prerequisite validation and provision tasks that require being signed into a Microsoft 365 tenant. - Run the new launch configuration
Attach to Frontend in Teams for GCC (Edge)
we just created that won’t tell Teams to start installing an app after the browser launches. - Run the existing launch configuration
Attach to Local Service
that will connect the VSCode debugger to the local Node process’s debugging port (9229
).
Within the compounds
array, locate and duplicate the existing compound configuration…
{
"name": "Debug in Teams (Edge)",
"configurations": [
"Attach to Frontend in Teams (Edge)",
"Attach to Local Service"
],
"preLaunchTask": "Start Teams App Locally",
"presentation": {
"group": "group 1: Teams",
"order": 1
},
"stopAll": true
},
… and make the following changes:
- Update the
name
toDebug in Teams for GCC (Edge)
- Change the first
configuration
in the array to use the one you created:Attach to Frontend in Teams for GCC (Edge)
- Change the
preLaunchTask
to use the task you created:Start Teams App Locally (for GCC)
Your compound configuration should look like this:
{
"name": "Debug in Teams for GCC (Edge)",
"configurations": [
"Attach to Frontend in Teams for GCC (Edge)",
"Attach to Local Service"
],
"preLaunchTask": "Start Teams App Locally (for GCC)",
"presentation": {
"group": "group 1: Teams",
"order": 1
},
"stopAll": true
},
With the files in the project that control how VS Code works, now you can start making changes to your project’s source code. Unfortunately, that same issue I previously mentioned about the TTK forcing you to be logged into a M365 tenant are going to keep us from using some of the more productive features the TTK VS Code extension and associated TTK CLI have to offer.
Some of the changes I’m going to suggest are good for documentation, but also in case in the future the TTK gives us a way to bypass that M365 login check.
Create a new Entra ID application
By default, the Teams tab project creates an Entra ID application used in a single sign-on (SSO) scenario for your Teams app. This is done using two actions: aadApp/create
and aadApp/update
.
These two actions creates the Entra ID app using the Microsoft Graph. You may or may not have access to this endpoint - it’s hard to find the documentation if it’s available in the GCC implementation of Microsoft Graph. But, you can delete this action if you’re in a GCC-HIGH or DoD cloud. The action is hard-coded to use the https://graph.microsoft.com
endpoint, but the GCC-HIGH & DoD clouds use different endpoints.
So to be safe, I’d manually create the Entra ID application using the Microsoft Entra ID admin center:
- Select Manage > App registrations in the left-hand navigation and then select New registration.
- On the Register an application page, set the values as follows, and then select Register:
- Name: Teams GCC Test
- Supported account types: Accounts in this organizational directory only (Single tenant)
Microsoft Entra ID will then display the details of the new app:
Create a client secret for the Entra ID app
For an app to authenticate using the OAuth2 client credentials flow with Microsoft Entra ID, it needs both the client ID and a client secret.
- Select Manage > Certificates and secrets from the left-hand navigation.
- On the Certificates & Secrets page, select the Client Secrets tab and then select New client secret.
- Set a description and select an expiration duration, then select Add.
- When the secret is created, it will be shown one time so make sure you copy this as you’ll need it in a moment.
Create a custom OAuth2 permission
Microsoft Teams needs a special permission (scope) defined on your app to obtain an access token to support SSO. To support this, you need to create the scope on your new app:
- Select Manage > Expose an API in the left-hand navigation, select the Set link next to the Application AD URI label. This will default the app’s ID to
api://<app-id>
. - Next, select Add a scope to add a new permission for the app. Create a new scope using the following settings and then select Add scope:
- Scope name: access_as_user
- Who can consent? Admins and users
- Admin consent title: Teams can access app’s web APIs
- Admin consent description: Allows Teams to call the app’s web APIs as the current user.
- User consent title: Teams can access app’s web APIs and make requests on your behalf
- User consent description: Enable Teams to call this app’s web APIs with the same rights that you have
- State: Enabled Next, select Add a scope to add a new permission for the app.
With the new scope get the ID of the new scope:
- Select Manage > Manifest in the left-hand navigation.
- Scroll down to the following property:
api.oauth2PermissionScopes.id
(there should be only one item in this array). - Copy the value of the
id
property as you’ll need it in the next step:
Go back to the app details page for the next step by selecting Overview in the left-hand navigation Keep this page open as you’ll need the values in the next step.
Update the local environment config
The local environment configuration files, ./env/.env.local and ./env/.env.local.user are used to configure the local development process.
Update the Entra ID app’s details
Start by manually updating the following values in the .env.local file for the Entra ID application you just created using the value on the app details page in the Entra ID admin center:
.env.local | Entra ID app value |
---|---|
AAD_APP_ACCESS_AS_USER_PERMISSION_ID | Value of the id property for the of the access_as_user scope you created in the last step. |
TEAMS_APP_TENANT_ID | Directory (tenant) ID |
AAD_APP_CLIENT_ID | Application (client) ID |
AAD_APP_OBJECT_ID | Object ID |
AAD_APP_TENANT_ID | Directory (tenant) ID |
AAD_APP_OAUTH_AUTHORITY | https://login.microsoftonline.com/{{Directory (tenant) ID}} |
AAD_APP_OAUTH_AUTHORITY_HOST | https://login.microsoftonline.com |
Set the new Teams app’s ID
Finally, create a new GUID and set it to the TEAMS_APP_ID
environment variable in the .env.local file.
Update the Entra ID app’s secret details
Set the Entra ID app’s client secret by manually updating the SECRET_AAD_APP_CLIENT_SECRETenvironment variable
in the .env.local.user file for the Entra ID application you just created on the environment variable.
Update the local environment provision stage
The existing local project file, ./teamsapp.local.yml, is used to configure the local build. Many of the actions in the provision stage won’t work because you aren’t connected to your Microsoft 365 tenant.
Remove the following actions from the provision stage:
aadApp/create
teamsApp/create
aadApp/update
teamsApp/update
teamsApp/extendToM365
You should be left with only the following three actions:
script
teamsApp/validateManifest
teamsApp/zipAppPackage
Try it out!
At this point your project should be ready to try the changes out. You’ll still have a few manual steps to perform, but those will need to be done after you’ve run the build process.
Start the debugging process
In VS Code, from the Run and Debug menu item in the left-hand menu, select the launch configuration Debug in Teams for GCC (Edge) and then select the green start arrow:
After a few moments, you should see the browser launch & load Microsoft Teams (after first prompting you to sign in). Before you sign in, take a moment to update the Entra ID application.
Update the Entra ID application
Previously, you created a basic Entra ID application, but there are additional things you need to set. They couldn’t be set at the time because you needed some values added to your environment file. Some of these were set in the steps above while others were set as part of the provision stage.
Normally, the TTK does this as part of the provision stage using the aadApp/update
action, but we can’t rely on it for reasons i previously explained. One thing this action does do that’s very helpful is take a template Entra ID app manifest in the project (./aad.manifest.json) and update values in it from our environment variables. The action then takes this file and updates the existing Entra ID app using this manifest… but we can do that too!
Do the following steps to update the Entra ID application:
Locate and duplicate the ./aad.manifest.json file with the new name of ./aad.manifest.gcc.json. We’re going to make a change to the template but want to keep a copy of the original one in case we ever need it again.
Open the new ./aad.manifest.gcc.json file.
Find all instances of
${{
and replace them with{{
.We had to replace the${{
with{{
because the utility we’ll use only understands specific types of template syntaxes. The closest one for us is the Handlebars syntax{{
}}
, so we just stripped out the$
prefix before running the command.Find all instances of
permissionIds
and replace them withdelegatedPermissionIds
.Run the following command in the console from the root of your project:
npx envsub -s handlebars -f ./env/.env.local ./aad.manifest.gcc.json ./appPackage/build/aad.manifest.local.json
This will generate a new template you can use to update your Entra ID application. Open the ./appPackage/build/aad.manifest.local.json.
Using the Microsoft Entra ID admin center:
Select the application you previously created
Select Manage > Manifest in the left-hand navigation.
Locate the property
requestedAccessTokenVersion
and set it to2
.Locate the property
identifierUris
and replace it with the value of the same property in the aad.manifest.local.json file.Locate the array
preAuthorizedApplications
and replace it with the value of the same property in the aad.manifest.local.json file.Locate the array
web.redirectUriSettings
& update it using the following steps:In the aad.manifest.local.json file, locate the
replyUrlsWithType
property.Get the value of the
url
where thetype=Web
.Add a the following object to the manifest’s
web.redirectUriSettings
:{ "uri": "<<< value of uri copied in the step above >>>", "index": null }
Locate the array
spa.redirectUris
and add the values in the propertyreplyUrlsWithType.url
aad.manifest.local.json file where thetype
=Spa
.
At this point, your Entra ID app has been configured.
Sign-in to Microsoft Teams and install your app!
Now, you can finally install your Teams app!
Go back to the browser that VS Code launched and sign-in to Microsoft Teams.
At this point, provided have permissions to side load your application, you can upload the app directly to a team, channel, or selecting the Apps > Manage your Apps page in the Teams client where you can choose the option to Upload an app.
In this case, select the app package generated in your project: ./appPackage/build/appPackage.local.zip.
Conclusion
Yes, this process is quite tedious. It would be a lot easier if there was an option, maybe as an environment variable setting that would configure the TTK to skip steps that require you to be signed into Microsoft 365 tenants. Then, actions could be configured to skip those steps.
Or… Microsoft could get the necessary hooks for the TTK VS Code extension and CLI, like the Teams Developer Portal, deployed to GCC tenants.
Did you try these steps? Let me know if they worked (or not) for you by leaving a comment below!