<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Shakeel Osmani</title>
    <description>The latest articles on DEV Community by Shakeel Osmani (@rana_tigrina2002).</description>
    <link>https://dev.to/rana_tigrina2002</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4110154%2F20bd2e5a-1caa-4a9a-aa2f-0ccf86d2e589.png</url>
      <title>DEV Community: Shakeel Osmani</title>
      <link>https://dev.to/rana_tigrina2002</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rana_tigrina2002"/>
    <language>en</language>
    <item>
      <title>I built the Linux client for Azure Virtual Desktop that Microsoft never shipped</title>
      <dc:creator>Shakeel Osmani</dc:creator>
      <pubDate>Fri, 04 Sep 2026 17:11:22 +0000</pubDate>
      <link>https://dev.to/rana_tigrina2002/i-built-the-linux-client-for-azure-virtual-desktop-that-microsoft-never-shipped-219p</link>
      <guid>https://dev.to/rana_tigrina2002/i-built-the-linux-client-for-azure-virtual-desktop-that-microsoft-never-shipped-219p</guid>
      <description>&lt;p&gt;A native Linux client for Azure Virtual Desktop and Windows 365 with real feed discovery, Entra ID sign-in, and a hardened FreeRDP: packaged as a Flatpak.&lt;/p&gt;

&lt;p&gt;If you use Azure Virtual Desktop (AVD) or Windows 365 from a Linux machine, you already know the problem. Microsoft ships clients for Windows, macOS, iOS, Android, and the web. For Linux there is nothing: and the web client is what you fall back to when you've given up.&lt;/p&gt;

&lt;p&gt;The threads asking for a Linux client go back years. So I built one.&lt;/p&gt;

&lt;p&gt;AVD Feed + Connect is an unofficial, MIT-licensed, native Linux client. You sign in with your Entra ID account, it shows the desktops and remote apps you're entitled to: exactly like the Windows App does: and you double-click to connect.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatpak &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; https://shakeelosmani.github.io/avd_feed_connect/avd_feed_connect.flatpakref
flatpak run io.github.shakeelosmani.avd_feed_connect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the whole install. The rest of this post is about why the existing options fall short and what it took to close the gap.&lt;/p&gt;

&lt;p&gt;The problem isn't RDP. It's feed discovery.&lt;/p&gt;

&lt;p&gt;Linux has had a capable RDP client for a long time: FreeRDP. Recent versions even speak AVD's ARM gateway and Entra ID auth (/gateway:type:arm /sec:aad). So why can't you just use it?&lt;/p&gt;

&lt;p&gt;Because AVD isn't "RDP to a host." It's a feed. When you sign in on Windows, the client calls a discovery endpoint, gets back the list of workspaces you're assigned, and for each resource it downloads an .rdp file that carries a per-resource gateway address and identity. Those files are what FreeRDP needs, and nothing on Linux fetches them for you.&lt;/p&gt;

&lt;p&gt;The workarounds people use today:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Web client:&lt;/strong&gt; works, but you lose local audio devices, camera, multi-monitor, clipboard richness, and it's a browser tab.&lt;br&gt;
&lt;strong&gt;Hand-authored .rdp files:&lt;/strong&gt; you export one from a Windows machine per host pool, hope it doesn't change, and pass it to FreeRDP. Fragile, and impossible if you don't have a Windows box.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remmina / third-party clients:&lt;/strong&gt; they wrap FreeRDP but still don't do feed discovery.&lt;/p&gt;

&lt;p&gt;Feed discovery is the missing 20% that makes the other 80% usable. That's what this app adds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The app is Python + GTK, and it does four things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Interactive Entra ID sign-in. It uses the OAuth2 authorization-code flow with PKCE inside an embedded WebKit view: the same flow the official clients use. This matters more than it sounds: many tenants have Conditional Access policies that block the device-code flow, which is what most CLI tools reach for. Doing the real interactive flow means the app works in locked-down enterprise tenants instead of failing at sign-in.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Feed discovery. With a token in hand, it queries rdweb.wvd.microsoft.com/api/arm/feeddiscovery. The service checks for an approved X-MS-User-Agent header, so the app sends one it accepts. The response is your list of workspaces and resources, including the per-resource icons.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Workspace grid. Those resources are rendered in a grid with their real icons. Double-click to connect.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj7gn5loe2vqpkpcd0vqk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj7gn5loe2vqpkpcd0vqk.png" alt="AVD FEED + Connect App Screen" width="799" height="636"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Connect. The app downloads the resource's .rdp from the feed and launches a bundled sdl-freerdp with /gateway:type:arm /sec:aad. Token refresh uses the standard refresh_token grant (offline_access), so the access token is renewed silently before it expires and you aren't re-prompted to sign in every hour.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There's also a CLI if you'd rather script it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 src/avdfeed.py list        &lt;span class="c"&gt;# list workspaces&lt;/span&gt;
python3 src/avdfeed.py connect 0   &lt;span class="c"&gt;# connect to resource 0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why the bundled FreeRDP matters&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I could have shelled out to whatever xfreerdp is on your PATH. I didn't, because the stock build has two problems that make it painful for real work — specifically Teams calls, which is what most people are doing in these sessions.&lt;/p&gt;

&lt;p&gt;Audio hot-unplug freezes the session. Plug in or unplug headphones during a call and upstream FreeRDP's PulseAudio backend can hit a busy loop in rdpsnd that freezes the entire session. The fix is in FreeRDP#13334; the bundled build includes it.&lt;/p&gt;

&lt;p&gt;No camera redirection. The bundled build is compiled with CHANNEL_RDPECAM_CLIENT, so your webcam shows up inside the session. Microphone and multi-monitor are enabled too.&lt;/p&gt;

&lt;p&gt;Shipping it as a Flatpak means you get this specific FreeRDP regardless of what your distro packages, and it can't conflict with a system FreeRDP you use for other things.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install options&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One command (recommended). The .flatpakref above adds a small signed remote hosted on GitHub Pages, so the app updates with a normal flatpak update. Opening the file in a browser installs it through GNOME Software or your Flatpak handler. The GNOME 49 runtime is pulled from Flathub automatically.&lt;/p&gt;

&lt;p&gt;Single-file bundle. Grab avd_feed_connect.flatpak from the latest release and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatpak &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; avd_feed_connect.flatpak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build it yourself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatpak &lt;span class="nb"&gt;install &lt;/span&gt;flathub org.gnome.Platform//49 org.gnome.Sdk//49 org.flatpak.Builder
flatpak run org.flatpak.Builder &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;--install&lt;/span&gt; &lt;span class="nt"&gt;--force-clean&lt;/span&gt; build-dir &lt;span class="se"&gt;\&lt;/span&gt;
  io.github.shakeelosmani.avd_feed_connect.yml
flatpak run io.github.shakeelosmani.avd_feed_connect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unpackaged (development). The same code runs without Flatpak if you have PyGObject (GTK 3, WebKit2 4.1) and an SDL3 sdl-freerdp on PATH. AVD_TENANT and AVD_UPN override the tenant/account, and AVD_SDL_FREERDP points at a different client binary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Status and what's next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's early. Sign-in, feed discovery, the workspace grid, and connect all work. A Flathub listing is planned so it shows up in your software center like anything else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I'd most like help with&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Testing in tenants with unusual Conditional Access setups. I can only test against the policies I have access to.&lt;br&gt;
Distro coverage. It's Flatpak so it should be portable, but "should" is doing work in that sentence.&lt;br&gt;
Remote app (RAIL) polish. Full desktops are the main path today.&lt;/p&gt;

&lt;p&gt;If it works for you, or especially if it doesn't, open an issue: &lt;a href="https://github.com/shakeelosmani/avd_feed_connect" rel="noopener noreferrer"&gt;https://github.com/shakeelosmani/avd_feed_connect&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;This is an independent project, not affiliated with or endorsed by Microsoft.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>freerdp</category>
      <category>linux</category>
      <category>remotedesktop</category>
      <category>avd</category>
    </item>
  </channel>
</rss>
