M365 Show -  Microsoft 365 Digital Workplace Daily
M365 Show with Mirko Peters - Microsoft 365 Digital Workplace Daily
Dev Tunnels in Visual Studio for Microsoft 365 App Testing
0:00
-21:53

Dev Tunnels in Visual Studio for Microsoft 365 App Testing

Ever built the perfect Teams app locally… only to realize your customer can’t test it without a painful deployment? What if you could make your laptop act like a secure cloud endpoint in under 3 minutes? Dev Tunnels in Visual Studio can do exactly that—but only if you configure them right. Today I’ll walk you through how to open your local services to the internet, securely, for Microsoft 365 testing—and the one setting that could accidentally expose your entire dev box if you miss it.

What Dev Tunnels Really Are (and Why That Matters for M365 Apps)

Most developers hear “Dev Tunnels” and think “temporary URL.” That description isn’t wrong—it’s just incomplete. A tunnel isn’t just a link. It’s a remote entry point into whatever service is running on your machine right now. And when you’re building for Microsoft 365—whether that’s a Teams personal app, a SharePoint web part, or a Power Platform custom connector—that distinction matters a lot more than most people realize.

At its simplest, a Dev Tunnel is a way to take something running locally—say a Node server for your Teams tab—and let people hit it from anywhere on the internet. No publishing to Azure first. No staging environment. It’s just your local code, reachable by someone across the country the moment they load the link. For M365 scenarios, that’s gold. Teams tabs, SharePoint Framework solutions, or webhooks for Power Automate flows often need to make live calls to your dev service. Without an externally reachable endpoint, those features either break or force you into a slow deployment loop.

Here’s the catch—because tunnels are so easy to spin up, plenty of developers treat them as a disposable convenience. They’ll click “create,” grab the link, and forget about what’s actually being exposed. But the truth is, the way you configure your tunnel shapes who has access, how secure it is, and even how long it lasts. Without thinking about that, you might hand out more access than you intended—sometimes to the entire internet.

Think of it like issuing a guest pass to your dev machine. If that pass has “access all areas” printed on it, and you’ve left your desk unattended, you can imagine the risk. Dev Tunnels work the same way: the rules you set at creation time are the guardrails that keep your guests from wandering into places they shouldn’t.

I’ve seen people run right into this problem when testing with remote teammates. One dev tried to get feedback on a Teams tab from a colleague in another city. The app worked fine locally. But Teams, by design, wasn’t going to call `localhost:3000` from a user’s client session in another tenant. Without a tunnel, their only option was to package and deploy the tab into a test tenant running in Azure. That deployment cycle? Fifteen minutes per change. By lunchtime, they’d tested almost nothing. The first time they used a Dev Tunnel, feedback was instant—click save, reload in Teams, done.

Microsoft actually has a big base of developers using Microsoft 365 tools this way. A significant portion report that they can run most of their iteration cycles entirely locally, as long as they can push traffic through a tunnel. Those working cloud-only generally accept that slower loop as the trade-off. The tunnel group, in contrast, gets real-time feedback.

Before tunnels, devs relied on staging servers or manually deploying builds to cloud sandboxes. Staging works fine for stable features, but it’s overkill for testing a half-built card in Adaptive Cards Designer or checking if your bot’s messaging extension responds correctly when called from a remote Teams client. Not to mention that staging environments add network hops, authentication differences, and configuration mismatches that can hide or introduce issues you won’t see in production.

And it’s not just about speed. When you compress the feedback loop this much, collaboration changes. You can have a PM, a designer, and a developer looking at the same instance of your app in live Teams while you tweak things on your laptop. You’re building in real time, without waiting for a pipeline to run or an environment to reset. That leads to fewer surprises once you do publish.

So while “temporary URL” might be the simplest way to describe a Dev Tunnel, it barely scratches the surface. In Microsoft 365 development, they’re more like an on-demand extension of your developer environment into the outside world—one you can control down to the visibility, authentication, and lifespan. They’re not a side tool. They’re the connective tissue that makes secure, rapid iteration and real collaboration possible without blowing up your schedule in deployment waits.

And once you see them as that kind of infrastructure, the next question is pretty clear—how do you switch them on without wrecking the setup you already depend on?

Enabling Dev Tunnels in Visual Studio Without Breaking Your Setup

The first time you switch on a tunnel in Visual Studio, it feels almost effortless. A couple of clicks, and suddenly your dev box is reachable from anywhere. But that’s also when you notice your localhost SSL prompt breaking, your configured URLs no longer lining up, or your OAuth redirect URIs throwing errors. The magic moment turns into a head‑scratch fast if you’re not intentional about the setup.

Starting from a clean project, enabling a tunnel is straightforward. Open your solution, go to the project node in Solution Explorer, right‑click, and choose Properties. In web‑friendly projects, you’ll see a Debug tab. That’s where the Dev Tunnel option lives. Click the checkbox to enable a tunnel, and Visual Studio will prompt you for a tunnel name and scope. This is where most people just type something quick and hit Enter. But what you name and how you scope it will shape how your tunnel behaves later—especially when testing Microsoft 365 apps.

By default, Visual Studio tends to pick a public visibility option if you don’t change it. That means anyone with the link can hit your endpoint. For a throwaway demo, maybe that’s fine. But if your project has authentication flows linked to an internal tenant or exposes an API behind that tunnel, you’ve effectively given the internet a way in. It’s all too common to see developers click through this without realizing they’ve left the door wide open.

In the visibility dropdown, you’ll see choices like Public, Public (authenticated), and Private (authenticated). Pick carefully—this isn’t just a label. For a Microsoft 365 Teams app that uses Entra ID, choosing an authenticated tunnel keeps your audience to those who can sign in with valid credentials. That makes accidental data exposure much less likely.

Once you’ve chosen scope, give your tunnel a clear and reusable name. This isn’t just cosmetic. If you use a consistent subdomain—for example, `myteamsapp.dev.tunnels.ms`—you can avoid constant adjustments to registered redirect URIs in Azure AD app registrations. OAuth callbacks are sensitive to exact URLs. If your tunnel address changes every test session, you’ll spend half your day in the Azure Portal re‑registering redirect URLs. Locking in a persistent tunnel name avoids that churn entirely.

Visually, the setup page is simple. Under the Dev Tunnel section, type your name, pick the scope, and hit “Apply.” Visual Studio will then spin up the tunnel the next time you debug. You’ll see a network forwarding table in the Output window showing your local port mapped to the `.dev.tunnels.ms` address.

But here’s something that trips up a lot of people: if you’re running IIS Express and enable a tunnel mid‑session, Visual Studio often restarts the web server. That restart kills your breakpoints for the current debugging run. If you were halfway through tracking down a tricky state bug, it’s frustrating to start over. The fix is simple—enable the tunnel before you start debugging. That way, the process starts in its final state, and your breakpoints hold as expected.

For multi‑port projects—say, you’ve got an API backend and a front‑end app—Visual Studio can only tunnel one port per project by default. If you need both exposed, you’ll have to either run them in separate projects with their own tunnels or use the CLI later for advanced config. Understanding this now helps you plan your debugging session without surprises.

Once configured, running your project now serves it both on `localhost` and via the external tunnel link. You can send that link to a colleague, paste it into your Teams configuration, or register it in your SharePoint Framework `serve.json` pointing to the externally accessible address. And because you set the visibility right, you know exactly who can reach it.

Done this way, a tunnel doesn’t disrupt your local SSL setup, it doesn’t kill your debugging context, and it doesn’t require repeated redirect URI edits. It just adds a secure, shareable endpoint on top of your normal workflow. That’s the sweet spot—a transparent addition to your process, not a new set of headaches.

Once you’ve got the tunnel running smoothly like this, the next big decision is the type of tunnel you choose—because that choice decides whether your remote testers are in a locked‑door environment or walking straight in off the street.

Choosing Between Public Anonymous and Private Authenticated Tunnels

Choosing the wrong tunnel type is basically like handing a stranger the keys to your office and hoping they only look at the thing you invited them to see. When Visual Studio prompts you to pick between Public Anonymous, Public Authenticated, and Private Authenticated, it’s not just a formality. Each choice changes what someone on the other end can do and how easily they can get in.

Public Anonymous is the fastest to set up. You create the tunnel, share the URL, and anyone with the link can hit your endpoint. There’s no sign‑in, no extra step for the tester. That speed is appealing during something like a hackathon, where your biggest concern is getting an early proof of concept in front of judges or teammates. You’re moving fast, the code is throwaway, and there’s no sensitive tenant data involved.

Public Authenticated takes that same open link but requires the user to log in with a Microsoft account before they can see your app. It’s a middle ground—good if you want to keep out completely anonymous access but don’t need to lock things to a single identity provider or narrow group. You’re adding a speed bump without fully closing the gate.

Private Authenticated is where you define a specific audience. Your testers have to authenticate, and they have to be in whatever group, tenant, or directory you’ve allowed. In the Microsoft 365 context, this often means they must sign in with an account in your Microsoft Entra ID tenant. That instantly cuts down who can see your app, and it ties access to an identity you already manage. If someone leaves your project or company, disabling their Entra ID account means their tunnel access disappears too—no separate cleanup needed.

Here’s where developers get tripped up. It’s common to default to Public Anonymous during early development because it’s frictionless. But if your code references internal APIs, even unintentionally, or your Teams app renders content from secure Graph endpoints, you’re exposing more than just a demo UI. That link could become an easy target for automated scanners or bots that sweep public URLs. Even if you think the risk is low, you have no visibility into who might be poking around.

Think about it in scenarios. At a hackathon, speed wins. You’re showing features, not production data, so Public Anonymous might be fine for those 24 hours. Compare that to pre‑release enterprise testing for a Teams app that’s wired into a finance system. In that case, Private Authenticated should be the default. Only invited testers can sign in, and their activity is logged through your normal Microsoft 365 auditing.

When deciding, break it into three questions: Who exactly needs to access this? What kind of data will they see or interact with? Do I need to verify their identity before they can connect? If the answers are “a small known group,” “potentially sensitive business data,” and “yes,” then Private Authenticated isn’t optional—it’s mandatory. If it’s “anyone,” “sample data only,” and “no,” then speed might win out with Public Anonymous, but you make that trade‑off consciously.

Entra ID integration is what makes Private Authenticated so strong for M365 work. It’s not just another login screen—it’s your tenant’s directory, with multi‑factor authentication and conditional access policies baked in. That means even in a tunnel context, you can enforce the same trust level as your production systems. Testers get in the same way they would access company resources, and you don’t have to invent a separate credential system for dev testing.

The security gap between public and authenticated options is bigger than most people assume. Public mode—anonymous or even with basic Microsoft account sign‑in—still faces the risk of targeted attacks if the URL gets shared or discovered. Authenticated tunnels tied to your directory shrink that risk to a controlled tester pool, and since every request is tied to a real account, you gain accountability.

Once you start thinking this way, picking a tunnel type stops being a “just click the first one” step and becomes part of your security posture. You’re no longer reacting after something gets exposed—you’re defining access at the moment you open the connection. And with that in place, the next logical step is figuring out how to create, manage, and reuse those exact configurations without manually clicking through Visual Studio every time. That’s where the CLI completely changes the game.

Taking Control with the Dev Tunnels CLI

The Visual Studio UI works fine when you’re setting up a single tunnel on your own machine. But if you’re trying to mirror that exact tunnel on multiple laptops, or spin one up from a build agent in seconds, the clicking becomes a bottleneck. That’s where the Dev Tunnels CLI starts to pull its weight. It gives you the same underlying tunnel service that Visual Studio uses, but with the repeatability and scripting control that’s hard to match in a GUI.

Most developers never open the CLI, so they miss out on a lot of practical capabilities. The UI is tied to the project you have open, which feels natural until you want a tunnel for something outside of Visual Studio or you need to reuse the same port without re‑configuring. The CLI doesn’t have those limits. You can start a tunnel without loading an IDE, you can standardise naming conventions, and you can automate the whole process so it’s consistent across every environment.

If you’ve never touched it before, the CLI approach is straightforward. Once you’ve installed the Dev Tunnels tool, creating a new tunnel is a single command. For example, `devtunnel create myteamsapp --port 3000 --allow-unauthenticated` will set up a new tunnel called “myteamsapp” forwarding port 3000 with anonymous access. From there, `devtunnel list` shows all your active and saved tunnels, along with their URLs. To start one, you use `devtunnel host myteamsapp`, and the service comes online instantly.

The naming here is more than cosmetic. Consistent names translate to consistent URLs, which is critical for any Microsoft 365 app that has registered redirect URIs or webhook endpoints. One wrong character in a callback URL, and your auth flow fails silently. With the CLI, you can define those names once and know that every developer on the team will get the same outcome.

Config files make this even smoother. You can store tunnel settings—name, port, authentication type—in a JSON file alongside your code. When a new developer joins the project, they run one command against that file, and their tunnel matches the rest of the team’s setup immediately. There’s no “go click here, then here, then change this dropdown” walkthrough. No step is left to memory.

This standardisation is where larger teams see real gains. Imagine onboarding three contractors into a Teams app project. Instead of each person fumbling through UI settings, they all run `devtunnel create --config devtunnel.json` and have the exact same environment in under a minute. That removes a whole category of “it works on my machine” problems before they start.

The CLI also opens the door to using Dev Tunnels in CI/CD pipelines. If your automated build runs integration tests that rely on an external callback—maybe a Teams messaging extension that needs to call your dev API—you can have the pipeline spin up a tunnel automatically during the job. The build agent hosts it, the tests run against the real endpoint, and when the job finishes, the tunnel is torn down. You get end‑to‑end testing with live callbacks without exposing anything permanently to the internet.

For local debugging, this flexibility means you’re not tied to having Visual Studio up and running just to serve traffic. You can host the backend through the CLI, then hit it from your front‑end app that’s running in another dev tool entirely. That makes it easier to test scenarios where one part of the solution is in .NET and another is in Node, without forcing both into the same debugging session. And because the CLI can reuse previous tunnels without changing URLs, your OAuth and webhook registrations stay valid between runs.

All of this adds up to a key point: the CLI isn’t just for people who dislike GUIs. It’s for anyone who needs repeatability, speed, and environment parity without relying on manual clicks. Once you’ve got a set of tested CLI commands, you can share them in your repo’s README, embed them in npm scripts, or wire them into PowerShell build routines. That’s a level of control and consistency you simply can’t get if every tunnel is created from scratch by hand.

When you’re working this way, tunneling stops being an occasional tool and becomes part of the core workflow. Every machine can host the same configuration, automation can stand up tunnels in seconds, and you can scale testing to more people without slowing down setup. But with that power comes responsibility—because once your tunnels are this fast to deploy, you need to be deliberate about how you protect them from the moment they come online.

Locking It Down: Avoiding Common Security Mistakes

One missed setting can turn your dev box into an open buffet for attackers. It’s not an exaggeration—Dev Tunnels are essentially internet-facing endpoints into whatever you’re running locally, and if you treat them like a casual “share” link, you’re handing out access without tracking who’s walking through the door.

The most common mistakes keep repeating. Developers spin up a tunnel and leave it at the default visibility—often Public Anonymous—without considering that the link may travel further than intended. Or they finish testing, close their laptop, and forget the tunnel is still running hours later. In some cases, tunnels stay accessible overnight with services still responding. And then there’s weak authentication—using accounts without MFA or handing access to people outside a controlled tenant. The pattern is predictable: speed wins in the moment, and security gets added “later.”

It doesn’t take much for a small oversight to snowball. Picture this: a local API behind a public tunnel helps test a Teams messaging extension. That API trusts calls from Graph with a certain token. An attacker probing randomly finds your tunnel’s URL, hits a less protected endpoint, and extracts some internal IDs. Those IDs get used to craft an authenticated request upstream. Now you’ve got a compromised API key tied back to your connected M365 tenant. From one missed scope change to a potential breach—without you ever realising someone outside your circle connected.

I’ve seen log snippets where exposed tunnels started receiving GET requests from IP ranges in regions the developer had no connection to. Patterns like `/wp-login.php` and `/phpmyadmin` litter the logs—a clear sign someone’s scanning for common admin pages. None of those existed on the dev box, but the noise made it harder to spot requests that actually mattered. If there had been a real vulnerable endpoint, the story could have ended differently.

This is why the principle of least privilege applies as much here as it does to any cloud role assignment. Open as little as possible for as short a time as possible. If you only need port 3000 for a Teams client test, don’t forward your entire web stack. Limit visibility to a specific tenant or authorised accounts. And the moment testing is done, shut it down—don’t leave the channel open “just in case.”

Microsoft Entra ID plays a big role here for M365-focused work. If you set your tunnel to Private Authenticated and tie it to your Entra ID tenant, you immediately raise the bar for access. Multi-factor authentication can kick in for testers. Conditional access policies can require devices to meet compliance before they connect. It’s the same trust layer you’d rely on for production resources, applied to your ephemeral dev endpoint. That’s far more effective than a random password or relying on an unvalidated public login.

Logging and monitoring can feel like overkill for something temporary, but it pays off the first time you need to verify whether a tester actually saw the latest build or if a strange request came from your team or from outside. Tunnel logs give you request timestamps, source IPs, and hit endpoints. Pair them with your app logs, and you get a timeline you can actually trust. In a sensitive test cycle, that record is your safety net.

At a practical level, locking things down means having a short checklist you run through every time you start a tunnel: set the correct visibility, verify the authentication mode, confirm only the necessary ports are forwarded, and plan a clear stop time. If your work takes more than one session, reuse named tunnels with fixed URLs instead of rebuilding them publicly each day—especially if that rebuild defaults to open access.

The goal isn’t to make tunnelling cumbersome—it’s to make it conscious. When security settings are part of your standard flow, you stop treating them as an “extra” and start treating them like the foundation. That’s how you avoid the feeling of “it was only supposed to be a quick test” becoming “we might have just leaked production data.”

Once you bake those habits into your workflow, Dev Tunnels stop being a casual risk and start being a reliable part of your testing pipeline. Which brings us to the bigger picture—how they fit strategically into how you build and collaborate in Microsoft 365 without slowing down delivery.

Conclusion

Dev Tunnels aren’t just a handy trick to skip deployment—they’re a bridge that connects your local code to real users securely when you configure them with intention. In Microsoft 365 projects, that means faster collaboration without taking shortcuts that weaken security.

For your next Teams, SharePoint, or Power Platform build, try starting with a private, authenticated tunnel tied to your tenant. You’ll keep control while giving testers the same experience as production.

Next time, we’ll look at pairing tunnels with automated Teams app testing pipelines—so your local changes can be validated end‑to‑end without ever leaving your development environment.

Discussion about this episode

User's avatar