<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
<title>azureblog.co.uk</title>
<link>https://azureblog.co.uk/</link>
<description>What&#39;s new in Entra ID, Intune and Azure, what it means in practice, and the steps and scripts to act on it.</description>
<language>en-gb</language>
<lastBuildDate>Thu, 08 Oct 2026 23:46:59 GMT</lastBuildDate>
<atom:link href="https://azureblog.co.uk/feed.xml" rel="self" type="application/rss+xml"/>
<item>
<title>Road to 50: how this blog runs for about £1 a month</title>
<link>https://azureblog.co.uk/posts/road-to-50-running-this-blog-for-1-a-month/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/road-to-50-running-this-blog-for-1-a-month/</guid>
<pubDate>Thu, 08 Oct 2026 08:00:00 GMT</pubDate>
<category>Azure</category>
<description>Fifty posts in, here&#39;s exactly how azureblog.co.uk is built, hosted and published, and why the whole thing costs less than a coffee each month.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/road-to-50-running-this-blog-for-1-a-month.png" alt=""></p><p>This is post number fifty. It felt like the right moment to open up the bonnet and show how the site itself works, because it's a neat example of how far the free and near-free tiers in Azure can take you.</p>
<figure class="flow"><ol class="steps"><li><div class="node"><small>01</small>Write the post</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>02</small>git push to main</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>03</small>GitHub Actions builds</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>04</small>Static Web Apps edge</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>05</small>Live on azureblog.co.uk</div></li></ol><figcaption>Every post follows this path. There's no server to patch.</figcaption></figure>

<h2 id="the-bill">The bill</h2>
<div class="tablewrap"><table>
  <tr><th>Service</th><th>What it does</th><th>Monthly cost</th></tr>
  <tr><td>Static Web Apps (Free)</td><td>Hosts the site on a global edge network, with free managed SSL certificates and custom domains</td><td>£0</td></tr>
  <tr><td>Azure DNS</td><td>Hosts the DNS zone for the domain</td><td>about 40p, plus a few pence per million queries</td></tr>
  <tr><td>GitHub</td><td>Stores the source and runs the build and deployment workflow</td><td>£0</td></tr>
</table></div>
<p>The domain registration is the only other cost, and that's paid yearly to the registrar. Everything else rounds to well under £1 a month.</p>

<h2 id="the-architecture">The architecture</h2>
<p>There's no server, database or CMS. A small Node.js script turns a folder of posts into plain HTML files, and Static Web Apps serves them.</p>
<figure class="dg dg-layers-wrap"><div class="dg-layers"><div class="dg-layer" style="--depth:0"><div class="dg-lh"><b>Azure DNS</b><span>Apex and www records for azureblog.co.uk</span></div><div class="dg-layer" style="--depth:1"><div class="dg-lh"><b>Static Web Apps (Free)</b><span>Edge hosting, HTTPS certificates, headers, 404 page</span></div><div class="dg-layer" style="--depth:2"><div class="dg-lh"><b>dist/ folder</b><span>Plain HTML, CSS, JSON data and images. Nothing runs on the server</span></div></div></div></div></div><figcaption>Each layer only does one job.</figcaption></figure>
<ul>
  <li><b>Static Web Apps</b> serves the files from the edge. A small <code>staticwebapp.config.json</code> sets security headers, cache rules and the 404 page.</li>
  <li><b>Azure DNS</b> handles both the bare domain and <code>www</code>. Bare domains can't use a CNAME, and many registrars don't offer ALIAS records, so moving the zone into Azure DNS was the clean fix. The domain stays registered where it was; only the nameservers changed.</li>
  <li><b>GitHub Actions</b> runs the build and deploys the output with the official <code>Azure/static-web-apps-deploy</code> action.</li>
</ul>

<h2 id="what-the-build-does">What the build does</h2>
<p>Each post is a single HTML file with a small block of metadata at the top: title, date, tags and a one-line summary. The build script reads them all and produces:</p>
<ul>
  <li>a page for every post, tag and tool, with reading time worked out from the word count</li>
  <li>a social sharing image for every post, drawn as SVG and rendered to PNG</li>
  <li>the RSS feed, sitemap and a search index</li>
  <li>a calendar feed of Microsoft deadlines from the <a href="https://azureblog.co.uk/timeline/">timeline</a></li>
  <li>the diagrams in posts, which are written as a few lines of JSON and turned into HTML and SVG at build time</li>
</ul>
<p>The <a href="https://azureblog.co.uk/tools/">tools</a> run entirely in your browser. The <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="jwt" data-term="JWT" data-def="JSON Web Token: a signed, base64url-encoded token carrying claims such as audience, issuer and permissions. Entra access and ID tokens are JWTs.">JWT</span> and <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="saml" data-term="SAML" data-def="Security Assertion Markup Language: an XML-based single sign-on standard where Entra ID sends a signed assertion about the user to the app." data-post="/posts/saml-certificate-rollover/">SAML</span> decoders, the GUID lookup and the rest load a JSON file and do the work locally, so nothing you paste is sent anywhere.</p>

<h2 id="how-a-post-gets-published">How a post gets published</h2>
<figure class="dg dg-seq-wrap"><div class="dg-scroll"><svg class="dg-seq" viewBox="0 0 740 381" width="740" height="381" role="img" aria-label="Sequence diagram: Me, GitHub, Actions runner, Static Web Apps"><line class="life" x1="107.5" y1="46" x2="107.5" y2="373"/><rect class="actor" x="30" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="107.5" y="28" text-anchor="middle">Me</text><line class="life" x1="282.5" y1="46" x2="282.5" y2="373"/><rect class="actor" x="205" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="282.5" y="28" text-anchor="middle">GitHub</text><line class="life" x1="457.5" y1="46" x2="457.5" y2="373"/><rect class="actor" x="380" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="457.5" y="28" text-anchor="middle">Actions runner</text><line class="life" x1="632.5" y1="46" x2="632.5" y2="373"/><rect class="actor" x="555" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="632.5" y="28" text-anchor="middle">Static Web Apps</text><text class="lbl" x="195" y="84" text-anchor="middle">Push a new post to main</text><text class="num" x="115.5" y="94" text-anchor="start">1</text><line class="arrow" x1="107.5" y1="99" x2="273.5" y2="99"/><polygon class="head" points="282.5,99 272.5,94 272.5,104"/><text class="lbl" x="370" y="134" text-anchor="middle">Workflow starts</text><text class="num" x="290.5" y="144" text-anchor="start">2</text><line class="arrow" x1="282.5" y1="149" x2="448.5" y2="149"/><polygon class="head" points="457.5,149 447.5,144 447.5,154"/><rect class="note t-accent" x="332.5" y="172" width="250" height="29" rx="6"/><text class="lbl" x="457.5" y="190" text-anchor="middle">npm ci, then node build.js</text><text class="lbl" x="545" y="248" text-anchor="middle">Upload dist/ with the</text><text class="lbl" x="545" y="263" text-anchor="middle">deploy token</text><text class="num" x="465.5" y="273" text-anchor="start">4</text><line class="arrow" x1="457.5" y1="278" x2="623.5" y2="278"/><polygon class="head" points="632.5,278 622.5,273 622.5,283"/><rect class="note t-ok" x="486" y="301" width="250" height="29" rx="6"/><text class="lbl" x="611" y="319" text-anchor="middle">New version live at the edge</text></svg></div><figcaption>From commit to live in about a minute.</figcaption></figure>
<p>The workflow also runs once a day on a schedule, so anything date-based, like the countdowns on the timeline, stays current without a commit. No plugins to patch, no hosting control panel, no database backups. Git history is the backup, and rolling back a mistake is a revert.</p>

<h2 id="what-i-d-tell-anyone-starting-a-technical-blog">What I'd tell anyone starting a technical blog</h2>
<ul>
  <li><b>Start simpler than you think.</b> A static site is fast, secure and nearly free. Add a build step only when you have a reason to.</li>
  <li><b>Write the post you wish you'd found.</b> Most of the posts here started as an error message or a change notice that needed a clear explanation.</li>
  <li><b>Link to the source.</b> Microsoft changes things often. Every news post here links to the official documentation so readers can check the latest detail.</li>
  <li><b>Use your own platform.</b> Hosting an Azure blog on Azure keeps the skills sharp and gives you real-world material to write about.</li>
</ul>

<h2 id="next-fifty">Next fifty</h2>
<p>More of the same: what's changing in Entra ID, Intune and Azure, what it means in practice, and the scripts and steps to act on it. Thanks for reading.</p>
]]></content:encoded>
</item>
<item>
<title>Intune in September: deployment rings, faster compliance and stricter automation</title>
<link>https://azureblog.co.uk/posts/intune-september-2026-roundup/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/intune-september-2026-roundup/</guid>
<pubDate>Tue, 06 Oct 2026 08:00:00 GMT</pubDate>
<category>Intune</category><category>Windows</category><category>What&#39;s new</category>
<description>Five recent Intune changes that affect how you roll out, how quickly devices become compliant, and whether your automation keeps working.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/intune-september-2026-roundup.png" alt=""></p><h2 id="deployment-plans-proper-rings-built-in">Deployment plans: proper rings, built in</h2>
<p>The new <b>Deployments</b> experience lets you stage a rollout across multiple rings with controlled timing, instead of assigning straight to everyone or juggling pilot groups by hand. It works with <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="win32" data-term="Win32 app" data-def="A traditional Windows app packaged as an .intunewin file so Intune can install it with detection rules, dependencies and return codes." data-post="/posts/win32-app-packaging-detection-rules/">Win32 apps</span>, Enterprise App Catalog apps, Settings Catalog policies and endpoint security policies, and integrates with Multiple Admin Approval.</p>
<p>For anything risky, such as a new security baseline or a firewall policy change, this should become the default way you deploy.</p>
<figure class="dg dg-timeline-wrap"><ol class="dg-tl"><li class=""><span class="dg-dot"></span><time>Ring 0</time><span>IT devices, day 1</span></li><li class="t-accent"><span class="dg-dot"></span><time>Ring 1</time><span>Pilot users, day 3</span></li><li class=""><span class="dg-dot"></span><time>Ring 2</time><span>25% of devices, day 7</span></li><li class="t-ok"><span class="dg-dot"></span><time>Ring 3</time><span>Everyone, day 14</span></li></ol><figcaption>A typical ring plan for a security baseline.</figcaption></figure>

<h2 id="client-driven-compliance-evaluation-preview">Client-driven compliance evaluation (preview)</h2>
<p>Compliance on Windows has traditionally waited for the next check-in. Now supported Windows devices notice when a compliance signal changes, such as the firewall, antivirus, BitLocker, Defender status, OS build, real-time protection or Secure Boot, and ask Intune to re-evaluate straight away.</p>
<p>The practical benefit is speed. When a user fixes the problem that blocked them, they get access back faster. When a device falls out of compliance, <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="conditional-access" data-term="Conditional Access" data-def="Entra ID&#39;s policy engine. Each policy says: if these users sign in to these apps under these conditions, then require (or block) something, such as MFA or a compliant device." data-post="/posts/conditional-access-report-only-and-what-if/">Conditional Access</span> catches it sooner.</p>
<figure class="dg dg-compare-wrap"><div class="dg-compare" style="--cols:2"><div class="dg-col"><h4>Scheduled check-in</h4><ul><li>Device re-evaluates on its next check-in</li><li>Delay between fixing a setting and getting access</li></ul></div><div class="dg-col t-ok"><h4>Client-driven (preview)</h4><ul><li>Device notices a signal change</li><li>Asks Intune to re-evaluate straight away</li><li>Firewall, antivirus, BitLocker, Secure Boot…</li></ul></div></div><figcaption>Scheduled versus client-driven compliance.</figcaption></figure>

<h2 id="multi-admin-approval-now-covers-graph-automation">Multi Admin Approval now covers Graph automation</h2>
<p>Multi Admin Approval (MAA) used to apply only to changes made in the admin center. It now applies to <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="graph" data-term="Microsoft Graph" data-def="The single API for Microsoft 365, Entra ID and Intune data, also used by the Microsoft Graph PowerShell SDK.">Microsoft Graph</span> calls too. If you have MAA access policies protecting a resource, scripts and apps that change it without going through approval now get an <b>HTTP 403</b> error.</p>
<ul>
  <li>Check which automation touches MAA-protected resources, such as app deployments or scripts.</li>
  <li>Update those scripts to follow the MAA approval workflow.</li>
  <li>If you can't change an app yet, use the new <b>Exclusions</b> tab in the access policy to exempt it. Treat that as temporary.</li>
</ul>
<figure class="dg dg-seq-wrap"><div class="dg-scroll"><svg class="dg-seq" viewBox="0 0 670 397" width="670" height="397" role="img" aria-label="Sequence diagram: Script, Microsoft Graph, Multi Admin Approval"><line class="life" x1="125" y1="46" x2="125" y2="389"/><rect class="actor" x="30" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="125" y="28" text-anchor="middle">Script</text><line class="life" x1="335" y1="46" x2="335" y2="389"/><rect class="actor" x="240" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="335" y="28" text-anchor="middle">Microsoft Graph</text><line class="life" x1="545" y1="46" x2="545" y2="389"/><rect class="actor" x="450" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="545" y="28" text-anchor="middle">Multi Admin Approval</text><text class="lbl" x="230" y="84" text-anchor="middle">Change protected app</text><text class="lbl" x="230" y="99" text-anchor="middle">assignment</text><text class="num" x="133" y="109" text-anchor="start">1</text><line class="arrow" x1="125" y1="114" x2="326" y2="114"/><polygon class="head" points="335,114 325,109 325,119"/><text class="lbl" x="440" y="149" text-anchor="middle">Is this resource protected?</text><text class="num" x="343" y="159" text-anchor="start">2</text><line class="arrow" x1="335" y1="164" x2="536" y2="164"/><polygon class="head" points="545,164 535,159 535,169"/><text class="lbl" x="440" y="199" text-anchor="middle">Yes; no approval header</text><text class="num" x="537" y="209" text-anchor="end">3</text><line class="arrow t-bad" x1="545" y1="214" x2="344" y2="214"/><polygon class="head t-bad" points="335,214 345,209 345,219"/><text class="lbl" x="230" y="249" text-anchor="middle">HTTP 403</text><text class="num" x="327" y="259" text-anchor="end">4</text><line class="arrow t-bad" x1="335" y1="264" x2="134" y2="264"/><polygon class="head t-bad" points="125,264 135,259 135,269"/><rect class="note t-accent" x="4" y="287" width="250" height="59" rx="6"/><text class="lbl" x="129" y="305" text-anchor="middle">Fix: follow the approval</text><text class="lbl" x="129" y="320" text-anchor="middle">workflow, or exclude the app</text><text class="lbl" x="129" y="335" text-anchor="middle">temporarily</text></svg></div><figcaption>An automation call against a protected resource.</figcaption></figure>

<h2 id="windows-11-26h2-security-baseline">Windows 11 26H2 security baseline</h2>
<p>A new security baseline for Windows 11, version 26H2, is available. Existing baseline profiles <b>don't update automatically</b>. Create a new profile or update the version on an existing one, and compare the setting changes first. Deployment plans are a sensible way to roll it out.</p>

<h2 id="ios-ipados-18-is-now-the-minimum">iOS/iPadOS 18 is now the minimum</h2>
<p>Intune now requires iOS/iPadOS 18 or later for standard device management, Company Portal and app protection policies. Check your device inventory for older devices before users run into problems.</p>

<h2 id="checklist">Checklist</h2>
<ul class="check"><li>Plan a deployment plan for the next baseline or major policy change</li><li>Pilot client-driven compliance on a few Windows devices</li><li>Find scripts and apps that change MAA-protected resources</li><li>Create a new 26H2 baseline profile and compare settings</li><li>Report on iOS devices below version 18</li></ul>

<div class="sources"><b>Sources</b><ul>
  <li><a href="https://learn.microsoft.com/en-us/intune/whats-new/" target="_blank" rel="noopener">Microsoft Learn: What's new in Microsoft Intune</a></li>
  <li><a href="https://learn.microsoft.com/en-us/intune/device-management/deployments/overview" target="_blank" rel="noopener">Microsoft Learn: Deployment plans and deployments</a></li>
  <li><a href="https://learn.microsoft.com/en-us/intune/fundamentals/role-based-access-control/multi-admin-approval-graph-api" target="_blank" rel="noopener">Microsoft Learn: Multi Admin Approval with the Graph API</a></li>
</ul></div>
]]></content:encoded>
</item>
<item>
<title>Lock down app consent without blocking your users</title>
<link>https://azureblog.co.uk/posts/admin-consent-workflow-and-user-consent/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/admin-consent-workflow-and-user-consent/</guid>
<pubDate>Wed, 23 Sep 2026 08:00:00 GMT</pubDate>
<category>Entra ID</category><category>Security</category><category>Governance</category>
<description>Illicit consent grants are a favourite way to steal mailbox data. Restricting user consent and turning on the admin consent workflow closes the door without making life miserable.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/admin-consent-workflow-and-user-consent.png" alt=""></p><p>When a user signs in to a third-party app for the first time, the app can ask for permissions to their data, such as reading their mail or files. If users can grant any permission, an attacker only needs a convincing app and a phishing email. The resulting access survives password resets, because it isn't based on the password.</p>
<figure class="dg dg-seq-wrap"><div class="dg-scroll"><svg class="dg-seq" viewBox="0 0 670 368" width="670" height="368" role="img" aria-label="Sequence diagram: Attacker, User, Entra ID"><line class="life" x1="125" y1="46" x2="125" y2="360"/><rect class="actor" x="30" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="125" y="28" text-anchor="middle">Attacker</text><line class="life" x1="335" y1="46" x2="335" y2="360"/><rect class="actor" x="240" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="335" y="28" text-anchor="middle">User</text><line class="life" x1="545" y1="46" x2="545" y2="360"/><rect class="actor" x="450" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="545" y="28" text-anchor="middle">Entra ID</text><text class="lbl" x="230" y="84" text-anchor="middle">Phishing email: 'Open shared</text><text class="lbl" x="230" y="99" text-anchor="middle">document'</text><text class="num" x="133" y="109" text-anchor="start">1</text><line class="arrow" x1="125" y1="114" x2="326" y2="114"/><polygon class="head" points="335,114 325,109 325,119"/><text class="lbl" x="440" y="149" text-anchor="middle">Signs in to attacker's app</text><text class="num" x="343" y="159" text-anchor="start">2</text><line class="arrow" x1="335" y1="164" x2="536" y2="164"/><polygon class="head" points="545,164 535,159 535,169"/><text class="lbl" x="440" y="199" text-anchor="middle">Consent prompt: read your</text><text class="lbl" x="440" y="214" text-anchor="middle">mail</text><text class="num" x="537" y="224" text-anchor="end">3</text><line class="arrow t-accent" x1="545" y1="229" x2="344" y2="229"/><polygon class="head t-accent" points="335,229 345,224 345,234"/><text class="lbl" x="440" y="264" text-anchor="middle">Accepts</text><text class="num" x="343" y="274" text-anchor="start">4</text><line class="arrow t-bad" x1="335" y1="279" x2="536" y2="279"/><polygon class="head t-bad" points="545,279 535,274 535,284"/><text class="lbl" x="335" y="314" text-anchor="middle">Uses the token to read mail, even after a password reset</text><text class="num" x="133" y="324" text-anchor="start">5</text><line class="arrow t-bad" x1="125" y1="329" x2="536" y2="329"/><polygon class="head t-bad" points="545,329 535,324 535,334"/></svg></div><figcaption>How an illicit consent grant works, and why restricting consent stops it.</figcaption></figure>

<figure class="flow"><ol class="steps"><li><div class="node"><small>01</small>User signs in to new app</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>02</small>App asks for risky permission</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>03</small>User submits a request</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>04</small>Reviewer approves or denies</div></li></ol></figure>

<h2 id="1-restrict-user-consent">1. Restrict user consent</h2>
<p>In the Entra admin center, go to <b>Enterprise applications → Consent and permissions → User consent settings</b>. Choose:</p>
<ul>
  <li><b>Allow user consent for apps from verified publishers, for selected permissions</b>, and</li>
  <li>under <b>Permission classifications</b>, mark only low-risk permissions (such as <code>User.Read</code>, <code>openid</code>, <code>profile</code>, <code>email</code> and <code>offline_access</code>) as low impact.</li>
</ul>
<p>Users can still sign in to well-known apps that only need basic profile access. Anything riskier needs an admin.</p>

<h2 id="2-turn-on-the-admin-consent-workflow">2. Turn on the admin consent workflow</h2>
<p>Under <b>Enterprise applications → Admin consent settings</b>, enable <b>Users can request admin consent to apps they are unable to consent to</b>, and choose reviewers. Instead of a dead end, users see a request form, and reviewers get an email with the app and the permissions requested.</p>
<figure class="dg dg-seq-wrap"><div class="dg-scroll"><svg class="dg-seq" viewBox="0 0 670 433" width="670" height="433" role="img" aria-label="Sequence diagram: User, Entra ID, Reviewer"><line class="life" x1="125" y1="46" x2="125" y2="425"/><rect class="actor" x="30" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="125" y="28" text-anchor="middle">User</text><line class="life" x1="335" y1="46" x2="335" y2="425"/><rect class="actor" x="240" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="335" y="28" text-anchor="middle">Entra ID</text><line class="life" x1="545" y1="46" x2="545" y2="425"/><rect class="actor" x="450" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="545" y="28" text-anchor="middle">Reviewer</text><text class="lbl" x="230" y="84" text-anchor="middle">Tries a new app that needs</text><text class="lbl" x="230" y="99" text-anchor="middle">Mail.Read</text><text class="num" x="133" y="109" text-anchor="start">1</text><line class="arrow" x1="125" y1="114" x2="326" y2="114"/><polygon class="head" points="335,114 325,109 325,119"/><text class="lbl" x="230" y="149" text-anchor="middle">Approval required: request</text><text class="lbl" x="230" y="164" text-anchor="middle">it?</text><text class="num" x="327" y="174" text-anchor="end">2</text><line class="arrow" x1="335" y1="179" x2="134" y2="179"/><polygon class="head" points="125,179 135,174 135,184"/><text class="lbl" x="230" y="214" text-anchor="middle">Submits a justification</text><text class="num" x="133" y="224" text-anchor="start">3</text><line class="arrow" x1="125" y1="229" x2="326" y2="229"/><polygon class="head" points="335,229 325,224 325,234"/><text class="lbl" x="440" y="264" text-anchor="middle">Email with the app and</text><text class="lbl" x="440" y="279" text-anchor="middle">permissions</text><text class="num" x="343" y="289" text-anchor="start">4</text><line class="arrow" x1="335" y1="294" x2="536" y2="294"/><polygon class="head" points="545,294 535,289 535,299"/><text class="lbl" x="440" y="329" text-anchor="middle">Approves or denies</text><text class="num" x="537" y="339" text-anchor="end">5</text><line class="arrow t-ok" x1="545" y1="344" x2="344" y2="344"/><polygon class="head t-ok" points="335,344 345,339 345,349"/><text class="lbl" x="230" y="379" text-anchor="middle">Notified of the outcome</text><text class="num" x="327" y="389" text-anchor="end">6</text><line class="arrow" x1="335" y1="394" x2="134" y2="394"/><polygon class="head" points="125,394 135,389 135,399"/></svg></div><figcaption>With the workflow on, users ask instead of being blocked.</figcaption></figure>

<h2 id="3-review-what-s-already-granted">3. Review what's already granted</h2>
<p>Existing consents aren't affected by the new settings. Review the apps with risky delegated permissions such as <code>Mail.Read</code>, <code>Files.ReadWrite.All</code> or anything granted to all users:</p>
<div class="codebox"><div class="codebar"><span>powershell</span><button type="button" data-copy="" data-label="copy">copy</button></div><pre><code>Connect-MgGraph -Scopes "Directory.Read.All"

Get-MgOauth2PermissionGrant -All |
  Where-Object { $_.Scope -match "Mail\.|Files\.|Sites\.|Directory\." } |
  ForEach-Object {
    [pscustomobject]@{
      App         = (Get-MgServicePrincipal -ServicePrincipalId $_.ClientId).DisplayName
      ConsentType = $_.ConsentType
      Scope       = $_.Scope
    }
  } | Sort-Object App | Format-Table -AutoSize</code></pre></div>
<p>Revoke anything you don't recognise, and investigate how it got there.</p>
<div class="callout"><strong>Reviewer tip:</strong> before approving, check the publisher is verified, the permissions match what the app actually does, and someone in the business owns the request.</div>
<h2 id="what-reviewers-should-check">What reviewers should check</h2>
<ul class="check"><li>Is the publisher verified, and is it the company you expect?</li><li>Do the requested permissions match what the app does?</li><li>Is this a delegated or application permission?</li><li>Who in the business owns the request, and is there a contract?</li><li>Is there a narrower permission available?</li></ul>
<p>The <a href="https://azureblog.co.uk/tools/permissions/">Graph permission explainer</a> shows what each permission allows and suggests narrower alternatives.</p>
<h2 id="if-you-find-a-malicious-grant">If you find a malicious grant</h2>
<ol>
  <li>Remove the app's permissions and disable sign-in for the Enterprise App.</li>
  <li>Revoke the affected users' sessions.</li>
  <li>Check the audit log for when consent was granted and by whom.</li>
  <li>Check what the app accessed, using the mailbox and SharePoint audit logs.</li>
</ol>

]]></content:encoded>
</item>
<item>
<title>Unattended Graph PowerShell scripts with certificate authentication</title>
<link>https://azureblog.co.uk/posts/graph-powershell-app-only-certificate/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/graph-powershell-app-only-certificate/</guid>
<pubDate>Wed, 16 Sep 2026 08:00:00 GMT</pubDate>
<category>PowerShell</category><category>Entra ID</category><category>Security</category>
<description>Scheduled scripts can&#39;t answer an MFA prompt. App-only authentication with a certificate is the right way to run Microsoft Graph PowerShell unattended.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/graph-powershell-app-only-certificate.png" alt=""></p><p>Interactive <code>Connect-MgGraph</code> is fine at your desk. A scheduled task or server-side script needs its own identity. If the script runs in Azure, use a <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="managed-identity" data-term="Managed identity" data-def="An identity for an Azure resource whose credentials Azure creates and rotates for you, so code can reach other services without any stored secret." data-post="/posts/managed-identities-vs-service-principals/">managed identity</span>. Otherwise, use an <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="app-registration" data-term="App registration" data-def="The definition of an application in Entra ID: its ID, redirect URIs, credentials and the permissions it asks for.">app registration</span> with a <b>certificate</b>, never a client secret pasted into the script.</p>
<figure class="dg dg-compare-wrap"><div class="dg-compare" style="--cols:2"><div class="dg-col t-accent"><h4>Delegated (interactive)</h4><ul><li>Someone signs in</li><li>Acts as that user</li><li>Limited to what the user can access</li><li>MFA prompts apply</li></ul></div><div class="dg-col t-ok"><h4>App-only</h4><ul><li>No user involved</li><li>Acts as the app</li><li>Limited to the app permissions granted</li><li>Needs a certificate or managed identity</li></ul></div></div><figcaption>Interactive versus app-only sign-in.</figcaption></figure>

<h2 id="1-create-a-certificate">1. Create a certificate</h2>
<p>For a script on a Windows server, a self-signed certificate in the machine store works well:</p>
<div class="codebox"><div class="codebar"><span>powershell</span><button type="button" data-copy="" data-label="copy">copy</button></div><pre><code>$cert = New-SelfSignedCertificate -Subject "CN=Graph-UserReport" `
  -CertStoreLocation "Cert:\LocalMachine\My" `
  -KeyExportPolicy NonExportable -KeySpec Signature `
  -NotAfter (Get-Date).AddYears(1)

Export-Certificate -Cert $cert -FilePath C:\Temp\Graph-UserReport.cer</code></pre></div>
<p>Only the public key (<code>.cer</code>) leaves the server. The private key stays put and can't be exported.</p>

<h2 id="2-register-the-app">2. Register the app</h2>
<ol>
  <li>Create an app registration, single tenant.</li>
  <li>Under <b>Certificates &amp; secrets</b>, upload the <code>.cer</code> file.</li>
  <li>Under <b>API permissions</b>, add the <b>Application</b> permissions the script needs, such as <code>User.Read.All</code>, and grant admin consent. Keep it to the minimum.</li>
  <li>Add at least two owners.</li>
</ol>

<h2 id="3-connect-in-the-script">3. Connect in the script</h2>
<div class="codebox"><div class="codebar"><span>powershell</span><button type="button" data-copy="" data-label="copy">copy</button></div><pre><code>Connect-MgGraph -ClientId "&lt;app-id&gt;" -TenantId "&lt;tenant-id&gt;" `
  -CertificateThumbprint "&lt;thumbprint&gt;" -NoWelcome

Get-MgUser -All -Property DisplayName,UserPrincipalName,AccountEnabled |
  Export-Csv C:\Reports\users.csv -NoTypeInformation</code></pre></div>
<figure class="dg dg-seq-wrap"><div class="dg-scroll"><svg class="dg-seq" viewBox="0 0 740 461" width="740" height="461" role="img" aria-label="Sequence diagram: Script, Local cert store, Entra ID, Microsoft Graph"><line class="life" x1="107.5" y1="46" x2="107.5" y2="453"/><rect class="actor" x="30" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="107.5" y="28" text-anchor="middle">Script</text><line class="life" x1="282.5" y1="46" x2="282.5" y2="453"/><rect class="actor" x="205" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="282.5" y="28" text-anchor="middle">Local cert store</text><line class="life" x1="457.5" y1="46" x2="457.5" y2="453"/><rect class="actor" x="380" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="457.5" y="28" text-anchor="middle">Entra ID</text><line class="life" x1="632.5" y1="46" x2="632.5" y2="453"/><rect class="actor" x="555" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="632.5" y="28" text-anchor="middle">Microsoft Graph</text><text class="lbl" x="195" y="84" text-anchor="middle">Looks up certificate by</text><text class="lbl" x="195" y="99" text-anchor="middle">thumbprint</text><text class="num" x="115.5" y="109" text-anchor="start">1</text><line class="arrow" x1="107.5" y1="114" x2="273.5" y2="114"/><polygon class="head" points="282.5,114 272.5,109 272.5,119"/><rect class="note" x="4" y="137" width="250" height="44" rx="6"/><text class="lbl" x="129" y="155" text-anchor="middle">Signs a client assertion with</text><text class="lbl" x="129" y="170" text-anchor="middle">the private key</text><text class="lbl" x="282.5" y="228" text-anchor="middle">Requests token for the app</text><text class="num" x="115.5" y="238" text-anchor="start">3</text><line class="arrow" x1="107.5" y1="243" x2="448.5" y2="243"/><polygon class="head" points="457.5,243 447.5,238 447.5,248"/><rect class="note t-ok" x="332.5" y="266" width="250" height="44" rx="6"/><text class="lbl" x="457.5" y="284" text-anchor="middle">Checks signature against the</text><text class="lbl" x="457.5" y="299" text-anchor="middle">uploaded public key</text><text class="lbl" x="282.5" y="357" text-anchor="middle">Access token with application permissions</text><text class="num" x="449.5" y="367" text-anchor="end">5</text><line class="arrow" x1="457.5" y1="372" x2="116.5" y2="372"/><polygon class="head" points="107.5,372 117.5,367 117.5,377"/><text class="lbl" x="370" y="407" text-anchor="middle">Calls Graph</text><text class="num" x="115.5" y="417" text-anchor="start">6</text><line class="arrow t-ok" x1="107.5" y1="422" x2="623.5" y2="422"/><polygon class="head t-ok" points="632.5,422 622.5,417 622.5,427"/></svg></div><figcaption>What happens when the script connects.</figcaption></figure>

<h2 id="keep-it-safe">Keep it safe</h2>
<ul>
  <li>Run the scheduled task as an account that can read the certificate's private key, and no more.</li>
  <li>Track the certificate's expiry date and renew it before it lapses.</li>
  <li>Watch the app's sign-ins in the <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="service-principal" data-term="Service principal" data-def="An app&#39;s identity inside one tenant. An app registration is the global definition; the service principal is the local instance that gets permissions and signs in." data-post="/posts/managed-identities-vs-service-principals/">service principal</span> sign-in logs.</li>
  <li>Consider limiting the app with a <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="conditional-access" data-term="Conditional Access" data-def="Entra ID&#39;s policy engine. Each policy says: if these users sign in to these apps under these conditions, then require (or block) something, such as MFA or a compliant device." data-post="/posts/conditional-access-report-only-and-what-if/">Conditional Access</span> policy for <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="workload-identity" data-term="Workload identity" data-def="Any non-human identity, such as an app, service principal or managed identity, that signs in to access resources.">workload identities</span> if you have the licensing.</li>
</ul>
<h2 id="renewing-the-certificate">Renewing the certificate</h2>
<ol>
  <li>About a month before expiry, create a new certificate on the server, as before.</li>
  <li>Upload the new <code>.cer</code> to the app registration. Both certificates now work.</li>
  <li>Update the thumbprint in the script, or the scheduled task's parameter, and test a run.</li>
  <li>Remove the old certificate from the app registration and the server.</li>
</ol>
<h2 id="troubleshooting">Troubleshooting</h2>
<ul>
  <li><b>AADSTS700027 (client assertion failed signature validation):</b> the certificate on the server doesn't match one uploaded to the app, or it has expired.</li>
  <li><b>Certificate not found:</b> the account running the task can't see it. Check the store (LocalMachine or CurrentUser) and the private key permissions.</li>
  <li><b>403 Forbidden from Graph:</b> the app is missing an application permission, or admin consent wasn't granted.</li>
</ul>

]]></content:encoded>
</item>
<item>
<title>Microsoft is retiring its own SMS and voice MFA. Here&#39;s your plan.</title>
<link>https://azureblog.co.uk/posts/entra-sms-voice-mfa-retirement/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/entra-sms-voice-mfa-retirement/</guid>
<pubDate>Fri, 04 Sep 2026 08:00:00 GMT</pubDate>
<category>Entra ID</category><category>Security</category><category>Passkeys</category><category>What&#39;s new</category>
<description>Microsoft-provided text and phone call authentication in Entra ID ends for most users on 1 February 2027. Passkeys are already being switched on. What changes, when, and what to do now.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/entra-sms-voice-mfa-retirement.png" alt=""></p><p>This is the biggest authentication change in Entra ID for years. Microsoft is retiring the SMS and voice call authentication it delivers itself, and is pushing users towards <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="passkey" data-term="Passkey" data-def="A FIDO2 credential made of a key pair. The private key stays on the device or in a password manager and only signs in to the site it was created for." data-post="/posts/synced-passkeys-and-passkey-profiles/">passkeys</span> instead. If any of your users still verify with a text message or a phone call, this affects you.</p>
<figure class="flow"><ol class="steps"><li><div class="node"><small>01</small>SMS or voice MFA today</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>02</small>1 Sep 2026: passkeys auto-enabled</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>03</small>Registration campaign nudges users</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>04</small>1 Feb 2027: blocking prompt</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>05</small>1 Jul 2027: admins and external users</div></li></ol><figcaption>Global Administrators and external users get the later date.</figcaption></figure>

<h2 id="what-s-actually-retiring">What's actually retiring</h2>
<p>The change covers <b>Microsoft-provided</b> SMS and voice: the codes and calls Microsoft's own telephony service sends. It applies to SMS and voice in the Authentication Methods Policy, to the legacy <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="mfa" data-term="MFA" data-def="Multifactor authentication: proving who you are with more than one factor, such as something you know, something you have, or something you are.">MFA</span> settings, and across Entra including self-service password reset.</p>
<p>Two things are <b>not</b> retiring. External MFA methods are unaffected. And organisations that genuinely need SMS or voice can keep it by contracting with a third-party telephony provider through the Microsoft Security Store.</p>
<p>The scope is the public cloud. Government clouds follow later, Azure AD B2C is out of scope, and External ID is getting its own announcement.</p>

<h2 id="the-timeline">The timeline</h2>
<div class="tablewrap"><table>
  <tr><th>Date</th><th>What happens</th></tr>
  <tr><td>1 Sep 2026</td><td>Passkeys are switched on automatically for users enabled for SMS or voice. A Microsoft-managed registration campaign nudges them to register a passkey at their next MFA sign-in. They can snooze it.</td></tr>
  <tr><td>30 Oct 2026</td><td>Third-party telephony providers become configurable in the Microsoft Security Store.</td></tr>
  <tr><td>1 Feb 2027</td><td>Retirement for all users except Global Administrators and external users. Anyone whose only method is SMS or voice gets a blocking prompt to register a passkey.</td></tr>
  <tr><td>1 Jul 2027</td><td>Retirement for Global Administrators and external users.</td></tr>
</table></div>
<p>Users won't be locked out. Anyone still relying only on SMS or voice gets a registration prompt they can't skip, and must set up a passkey before carrying on. For a busy service desk, that's still a Monday morning you want to avoid.</p>
<figure class="dg dg-timeline-wrap"><ol class="dg-tl"><li class="t-ok"><span class="dg-dot"></span><time>1 Sep 2026</time><span>Passkeys auto-enabled; registration nudges start</span></li><li class=""><span class="dg-dot"></span><time>30 Oct 2026</time><span>Third-party telephony providers available</span></li><li class="t-bad"><span class="dg-dot"></span><time>1 Feb 2027</time><span>Retirement for most users</span></li><li class="t-bad"><span class="dg-dot"></span><time>1 Jul 2027</time><span>Global Admins and external users</span></li></ol><figcaption>Key dates from Microsoft's SMS and voice retirement FAQ.</figcaption></figure>

<h2 id="what-to-do-now">What to do now</h2>
<ol>
  <li><b>Measure your exposure.</b> Microsoft publishes an <code>entra-sms-voice-usage-analyzer</code> PowerShell script on GitHub. It reports which users are in scope of SMS and voice in your authentication methods policies. Any non-zero result means your tenant is affected.</li>
  <li><b>Decide passkeys or provider.</b> Without a regulatory or operational reason to keep SMS, plan for passkeys. If you do need SMS, line up a telephony provider and move users before their retirement date.</li>
  <li><b>Plan your passkey rollout.</b> Use passkey profiles to set different rules for admins and standard users, and decide whether synced passkeys (stored in a passkey provider and shared across devices) are acceptable for each group.</li>
  <li><b>Use the registration campaign.</b> It now supports passkeys, so users are prompted at sign-in rather than chased by email.</li>
  <li><b>Don't forget the edges.</b> Shared mailboxes with MFA, <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="break-glass" data-term="Break-glass account" data-def="An emergency cloud-only Global Administrator account, excluded from normal policies and closely monitored, used only when normal admin access fails." data-post="/posts/break-glass-accounts-done-right/">break-glass accounts</span>, frontline workers without smartphones and guests all need a plan.</li>
</ol>
<h2 id="who-is-affected-in-practice">Who is affected, in practice</h2>
<p>The users who feel this most are the ones who have <b>only</b> SMS or voice registered. Anyone who already has Microsoft Authenticator, a passkey or <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="whfb" data-term="Windows Hello for Business" data-def="Phishing-resistant sign-in to Windows using a PIN or biometrics, backed by a key held in the device&#39;s TPM.">Windows Hello for Business</span> simply carries on with those. So the real work is finding the SMS-only group and moving them before the deadline.</p>
<figure class="dg dg-decision-wrap"><div class="dg-decision"><div class="dg-q">What methods does the user have registered?</div><div class="dg-branches"><div class="dg-branch t-ok"><span class="dg-if">Passkey, Authenticator or WHfB</span><span class="dg-then">Nothing to do. They keep signing in as normal.</span></div><div class="dg-branch t-accent"><span class="dg-if">SMS or voice plus something else</span><span class="dg-then">Remove reliance on SMS; nudge them to a passkey.</span></div><div class="dg-branch t-bad"><span class="dg-if">SMS or voice only</span><span class="dg-then">Must register a passkey before their retirement date, or get a blocking prompt.</span></div></div></div><figcaption>Which path each user takes.</figcaption></figure>
<p>Typical SMS-only groups are frontline staff without company phones, people who joined before Authenticator was standard, and shared or service-style accounts that someone set up with a phone number years ago.</p>
<h2 id="a-rollout-plan-that-works">A rollout plan that works</h2>
<ol>
  <li><b>Weeks 1 to 2: measure.</b> Run the analyzer script, then export the authentication methods registration report to get a named list of SMS-only users.</li>
  <li><b>Weeks 2 to 3: decide the exceptions.</b> Agree how you'll handle users with no smartphone: a <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="fido2" data-term="FIDO2" data-def="The open standard behind passkeys and security keys, combining WebAuthn in the browser with CTAP for authenticators.">FIDO2</span> security key, Windows Hello for Business on a managed PC, or a third-party SMS provider.</li>
  <li><b>Weeks 3 to 4: communicate.</b> Tell users what a passkey is and what the prompt will look like, before they see it.</li>
  <li><b>Weeks 4 to 8: campaign.</b> Let the registration campaign prompt people at sign-in. Limit snoozes once most users have registered.</li>
  <li><b>Ongoing: chase the tail.</b> Contact the remaining SMS-only users directly, and issue Temporary Access Passes where people are stuck.</li>
</ol>
<div class="callout"><strong>Ready-made emails:</strong> the <a href="https://azureblog.co.uk/tools/comms/">user comms templates</a> include "Moving to passkeys" and "Text message codes are going away".</div>

<h2 id="need-more-time">Need more time?</h2>
<p>There's a temporary opt-out that delays the automatic passkey enablement and registration campaign until 1 February 2027, while you set up a provider or migrate users. It's set through Graph:</p>
<div class="codebox"><div class="codebar"><span>http</span><button type="button" data-copy="" data-label="copy">copy</button></div><pre><code>PATCH https://graph.microsoft.com/beta/policies/authenticationmethodspolicy
Content-Type: application/json

{
  "optOutSettings": {
    "passkeyDynamicMigration": true
  }
}</code></pre></div>
<div class="callout"><strong>Important:</strong> the opt-out only delays the rollout. It does not stop enforcement. From 1 February 2027 the retirement applies regardless.</div>

<h2 id="questions-people-ask">Questions people ask</h2>
<details class="faq"><summary>Will users be locked out on 1 February 2027?</summary><p>No. Users who still rely only on Microsoft-provided SMS or voice get a blocking prompt to register a passkey and can carry on once they have.</p></details>
<details class="faq"><summary>Is SMS still allowed for self-service password reset?</summary><p>Microsoft's FAQ says the retirement applies across Entra, including SSPR. Organisations that need SMS can use a telephony provider from the Microsoft Security Store.</p></details>
<details class="faq"><summary>Does this affect external MFA providers?</summary><p>No. External MFA methods aren't affected, unless the same users are also enabled for Microsoft's SMS or voice.</p></details>
<details class="faq"><summary>Does the opt-out stop the retirement?</summary><p>No. It only delays automatic passkey enablement and the registration campaign until 1 February 2027.</p></details>

<div class="sources"><b>Sources</b><ul>
  <li><a href="https://learn.microsoft.com/en-us/entra/identity/authentication/concept-sms-voice-retirement-faq" target="_blank" rel="noopener">Microsoft Learn: SMS and voice retirement FAQ</a></li>
  <li><a href="https://github.com/microsoft/entra-sms-voice-usage-analyzer" target="_blank" rel="noopener">GitHub: entra-sms-voice-usage-analyzer</a></li>
  <li><a href="https://learn.microsoft.com/en-us/entra/identity/authentication/how-to-authentication-passkeys-fido2" target="_blank" rel="noopener">Microsoft Learn: Enable passkeys (FIDO2) in Entra ID</a></li>
</ul></div>
]]></content:encoded>
</item>
<item>
<title>Intune in August: unattended Remote Help, DDM app installs and eSIM</title>
<link>https://azureblog.co.uk/posts/intune-august-2026-roundup/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/intune-august-2026-roundup/</guid>
<pubDate>Wed, 02 Sep 2026 08:00:00 GMT</pubDate>
<category>Intune</category><category>What&#39;s new</category>
<description>Helpdesk agents can now support Windows devices with nobody at the keyboard, Apple VPP apps can install through declarative device management, and corporate Android gets eSIM control.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/intune-august-2026-roundup.png" alt=""></p><h2 id="unattended-remote-help-for-windows">Unattended Remote Help for Windows</h2>
<p>Remote Help on physical Windows devices now supports <b>unattended</b> sessions. Helpdesk agents sign in with their own credentials and connect without a user being present. It's ideal for kiosks, meeting room PCs and out-of-hours fixes. Lock it down with Remote Help roles and scope tags so only the right people can use it on the right devices.</p>
<figure class="dg dg-compare-wrap"><div class="dg-compare" style="--cols:2"><div class="dg-col t-accent"><h4>Attended</h4><ul><li>User is at the device</li><li>User accepts the session</li><li>Best for live troubleshooting</li></ul></div><div class="dg-col t-ok"><h4>Unattended</h4><ul><li>No one at the keyboard</li><li>Agent signs in with their own credentials</li><li>Kiosks, meeting rooms, out of hours</li></ul></div></div><figcaption>Attended and unattended sessions.</figcaption></figure>
<p>Because nobody approves an unattended session, permissions matter more. Use a dedicated Remote Help role, scope it with scope tags to the device groups that need it, and review the session reports regularly.</p>

<h2 id="declarative-device-management-for-vpp-apps">Declarative device management for VPP apps</h2>
<p>Apple Volume Purchase Program apps can now install through <b>declarative device management (DDM)</b>. Set the management type to <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="ddm" data-term="DDM" data-def="Declarative device management: Apple&#39;s newer management model where the device applies and reports on declared settings itself.">DDM</span> when you upload the VPP token. It requires iOS/iPadOS 17.2 or later, or macOS 26 or later. DDM lets devices act on their own state rather than waiting for commands, which tends to make installs faster and more reliable.</p>
<figure class="dg dg-compare-wrap"><div class="dg-compare" style="--cols:2"><div class="dg-col"><h4>Command-based (MDM)</h4><ul><li>Intune sends commands</li><li>Device reports back later</li><li>State can drift between check-ins</li></ul></div><div class="dg-col t-ok"><h4>Declarative (DDM)</h4><ul><li>Intune sends the desired state</li><li>Device applies and maintains it</li><li>Device reports status changes itself</li></ul></div></div><figcaption>Command-based versus declarative management.</figcaption></figure>

<h2 id="esim-management-for-corporate-android">eSIM management for corporate Android</h2>
<p>On corporate-owned Android devices, you can now activate eSIMs, remove individual eSIMs, and choose whether eSIMs are removed during a wipe. It requires Android 15 or later, or Android 17 for work profile devices, and uses the preview device view.</p>

<h2 id="also-new">Also new</h2>
<ul>
  <li><b>Audit mode for Defender Antivirus on Linux</b>, to detect threats without quarantining while you test.</li>
  <li><b>Apple OS 27 settings in beta</b> in the Settings Catalog, for testing ahead of release.</li>
  <li><b>A security baseline for Windows 365 for Agents.</b></li>
</ul>
<h2 id="what-to-do-this-month">What to do this month</h2>
<ul class="check"><li>Decide who may run unattended Remote Help, and set up roles and scope tags</li><li>Check your Apple devices meet the OS requirement before switching VPP tokens to DDM</li><li>Corporate Android: decide whether eSIMs should be removed on wipe</li><li>Test Apple OS 27 settings on beta devices only</li></ul>

<div class="sources"><b>Sources</b><ul><li><a href="https://learn.microsoft.com/en-us/intune/whats-new/" target="_blank" rel="noopener">Microsoft Learn: What's new in Microsoft Intune</a></li></ul></div>
]]></content:encoded>
</item>
<item>
<title>Stop surprise Azure bills with budgets and anomaly alerts</title>
<link>https://azureblog.co.uk/posts/azure-budgets-and-cost-anomaly-alerts/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/azure-budgets-and-cost-anomaly-alerts/</guid>
<pubDate>Wed, 26 Aug 2026 08:00:00 GMT</pubDate>
<category>Azure</category><category>Governance</category>
<description>Azure won&#39;t stop you spending, but it will tell you early if you set it up. Two features take five minutes and catch most surprises.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/azure-budgets-and-cost-anomaly-alerts.png" alt=""></p><h2 id="budgets">Budgets</h2>
<p>A budget tracks spending against an amount over a period and sends alerts when you cross thresholds. Create one in <b>Cost Management → Budgets</b> at subscription or resource group scope.</p>
<figure class="dg dg-seq-wrap"><div class="dg-scroll"><svg class="dg-seq" viewBox="0 0 740 456" width="740" height="456" role="img" aria-label="Sequence diagram: Usage, Cost Management, Budget, Action group"><line class="life" x1="107.5" y1="46" x2="107.5" y2="448"/><rect class="actor" x="30" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="107.5" y="28" text-anchor="middle">Usage</text><line class="life" x1="282.5" y1="46" x2="282.5" y2="448"/><rect class="actor" x="205" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="282.5" y="28" text-anchor="middle">Cost Management</text><line class="life" x1="457.5" y1="46" x2="457.5" y2="448"/><rect class="actor" x="380" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="457.5" y="28" text-anchor="middle">Budget</text><line class="life" x1="632.5" y1="46" x2="632.5" y2="448"/><rect class="actor" x="555" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="632.5" y="28" text-anchor="middle">Action group</text><text class="lbl" x="195" y="84" text-anchor="middle">Usage and charges</text><text class="lbl" x="195" y="99" text-anchor="middle">processed (not real</text><text class="lbl" x="195" y="114" text-anchor="middle">time)</text><text class="num" x="115.5" y="124" text-anchor="start">1</text><line class="arrow" x1="107.5" y1="129" x2="273.5" y2="129"/><polygon class="head" points="282.5,129 272.5,124 272.5,134"/><text class="lbl" x="370" y="164" text-anchor="middle">Compare actual and</text><text class="lbl" x="370" y="179" text-anchor="middle">forecast cost with</text><text class="lbl" x="370" y="194" text-anchor="middle">thresholds</text><text class="num" x="290.5" y="204" text-anchor="start">2</text><line class="arrow" x1="282.5" y1="209" x2="448.5" y2="209"/><polygon class="head" points="457.5,209 447.5,204 447.5,214"/><rect class="note t-accent" x="332.5" y="232" width="250" height="29" rx="6"/><text class="lbl" x="457.5" y="250" text-anchor="middle">Forecast passes 100% mid-month</text><text class="lbl" x="545" y="308" text-anchor="middle">Fire alert</text><text class="num" x="465.5" y="318" text-anchor="start">4</text><line class="arrow t-accent" x1="457.5" y1="323" x2="623.5" y2="323"/><polygon class="head t-accent" points="632.5,323 622.5,318 622.5,328"/><rect class="note t-ok" x="486" y="346" width="250" height="59" rx="6"/><text class="lbl" x="611" y="364" text-anchor="middle">Email the team, call a</text><text class="lbl" x="611" y="379" text-anchor="middle">webhook, run a runbook or</text><text class="lbl" x="611" y="394" text-anchor="middle">Logic App</text></svg></div><figcaption>How a budget alert reaches someone who can act on it.</figcaption></figure>

<ul>
  <li>Set thresholds on <b>actual</b> cost (such as 50%, 80% and 100%) and on <b>forecasted</b> cost (such as 100%). Forecast alerts warn you before the money is spent.</li>
  <li>Send alerts to a shared mailbox or an action group, not one person.</li>
  <li>Budgets alert; they don't stop anything. Use an action group to trigger automation if you need it, for example shutting down dev VMs.</li>
</ul>
<div class="codebox"><div class="codebar"><span>shell</span><button type="button" data-copy="" data-label="copy">copy</button></div><pre><code>az consumption budget create \
  --budget-name monthly-dev \
  --amount 500 \
  --category cost \
  --time-grain monthly \
  --start-date 2026-09-01 \
  --end-date 2027-08-31</code></pre></div>
<h3>Picking the thresholds</h3>
<figure class="dg dg-timeline-wrap"><ol class="dg-tl"><li class="t-accent"><span class="dg-dot"></span><time>Day 8</time><span>Forecast alert at 100%: on course to overspend, act now</span></li><li class=""><span class="dg-dot"></span><time>Day 15</time><span>Actual cost passes 50% (£250)</span></li><li class="t-accent"><span class="dg-dot"></span><time>Day 22</time><span>Actual cost passes 80% (£400): review what's running</span></li><li class="t-bad"><span class="dg-dot"></span><time>Day 28</time><span>Actual cost passes 100% (£500): automation stops dev VMs</span></li></ol><figcaption>A typical set of alerts across one month on a £500 budget.</figcaption></figure>
<p>The forecast alert is the most useful one. It arrives while there's still time to do something, rather than confirming the overspend after the fact.</p>
<h3>Scope and filters</h3>
<p>You can set budgets at <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="management-group" data-term="Management group" data-def="A container above subscriptions, used to apply policy and access to many subscriptions at once.">management group</span>, subscription or resource group scope, and filter them by tags, resource types or services. A filter on <code>env = dev</code> or a single expensive service gives a sharper alert than one big subscription number.</p>

<h2 id="anomaly-alerts">Anomaly alerts</h2>
<p>Cost Management can detect unusual spending patterns in a subscription automatically. Turn on <b>anomaly alerts</b> under <b>Cost alerts</b> to get an email when spending jumps beyond the normal pattern, even if you're well under budget. A runaway log ingestion or a forgotten large VM shows up as an anomaly within a day or so, not at the end of the month.</p>
<figure class="dg dg-compare-wrap"><div class="dg-compare" style="--cols:2"><div class="dg-col t-accent"><h4>Budget alerts</h4><p class="dg-sub">Am I going over the number I set?</p><ul><li>You choose the amount and thresholds</li><li>Actual and forecast cost</li><li>Any scope, with filters</li><li>Can trigger automation through action groups</li></ul></div><div class="dg-col t-ok"><h4>Anomaly alerts</h4><p class="dg-sub">Is this spend unusual?</p><ul><li>Learned from your spending pattern</li><li>Daily evaluation per subscription</li><li>Catches spikes well under budget</li><li>Email notification to the recipients you choose</li></ul></div></div><figcaption>Budgets and anomaly alerts answer different questions.</figcaption></figure>
<p>Anomaly detection learns what normal looks like for a subscription, so a new subscription needs some history before it's useful. It's a good partner to budgets because the two fail in different ways: a budget that's set too high never fires, and anomaly detection doesn't care what the budget says.</p>

<h2 id="make-costs-visible">Make costs visible</h2>
<ul>
  <li><b>Tag everything</b> with an owner and a cost centre. <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="azure-policy" data-term="Azure Policy" data-def="Rules that evaluate Azure resources and can audit, deny or automatically fix them, assigned at management group, subscription or resource group." data-post="/posts/azure-policy-allowed-locations-and-guardrails/">Azure Policy</span> can enforce and inherit tags.</li>
  <li><b>Schedule a cost report</b> by email to each team, grouped by their tag.</li>
  <li><b>Review Advisor cost recommendations</b> monthly for idle resources and right-sizing.</li>
</ul>
<h2 id="automating-a-response">Automating a response</h2>
<p>An action group can call a webhook, an Automation runbook, a Logic App or a Function. A common pattern for dev and test subscriptions:</p>
<ol>
  <li>Budget threshold at 100% of actual cost calls an action group.</li>
  <li>The action group starts an Automation runbook with a <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="managed-identity" data-term="Managed identity" data-def="An identity for an Azure resource whose credentials Azure creates and rotates for you, so code can reach other services without any stored secret." data-post="/posts/managed-identities-vs-service-principals/">managed identity</span>.</li>
  <li>The runbook deallocates VMs tagged <code>autoshutdown = true</code> and posts a message to the team.</li>
</ol>
<p>Keep automation to non-production. Stopping production workloads because of a cost alert is rarely the right trade.</p>
<h2 id="five-minute-checklist">Five-minute checklist</h2>
<ul class="check"><li>A budget on every subscription, with a forecast alert at 100%</li><li>Alerts go to a shared mailbox or action group, not one person</li><li>Anomaly alerts switched on for each subscription</li><li>Owner and cost-centre tags enforced by Azure Policy</li><li>A monthly scheduled cost view emailed to each team</li><li>Advisor cost recommendations reviewed once a month</li></ul>
<details class="faq"><summary>Will a budget stop Azure charging me?</summary><p>No. Budgets only alert. Spending limits exist on some offer types, such as free and credit-based subscriptions, but pay-as-you-go and enterprise subscriptions keep running.</p></details>
<details class="faq"><summary>Why did my alert arrive late?</summary><p>Cost data takes time to process, often several hours and sometimes up to a day. Budgets are an early warning, not a real-time meter.</p></details>

]]></content:encoded>
</item>
<item>
<title>Private endpoints and DNS: why your private endpoint isn&#39;t being used</title>
<link>https://azureblog.co.uk/posts/private-endpoints-dns/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/private-endpoints-dns/</guid>
<pubDate>Wed, 19 Aug 2026 08:00:00 GMT</pubDate>
<category>Azure</category><category>Security</category>
<description>You created a private endpoint, but traffic still goes to the public IP. It&#39;s almost always DNS. How private endpoint name resolution works and how to fix it.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/private-endpoints-dns.png" alt=""></p><p>A <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="private-endpoint" data-term="Private endpoint" data-def="A network interface with a private IP in your virtual network that connects to a PaaS service, such as Storage or Key Vault, over Azure&#39;s backbone.">private endpoint</span> gives a PaaS service, such as a storage account, SQL database or <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="key-vault" data-term="Key Vault" data-def="Azure&#39;s service for storing secrets, keys and certificates, with access controlled by Azure RBAC or legacy access policies." data-post="/posts/key-vault-rbac-vs-access-policies/">Key Vault</span>, a private IP address in your virtual network. Clients still connect using the normal public name, for example <code>stdata.blob.core.windows.net</code>. Whether they reach the private IP or the public one depends entirely on DNS.</p>
<figure class="dg dg-seq-wrap"><div class="dg-scroll"><svg class="dg-seq" viewBox="0 0 740 447" width="740" height="447" role="img" aria-label="Sequence diagram: On-prem client, On-prem DNS, Private Resolver, Private DNS zone"><line class="life" x1="107.5" y1="46" x2="107.5" y2="439"/><rect class="actor" x="30" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="107.5" y="28" text-anchor="middle">On-prem client</text><line class="life" x1="282.5" y1="46" x2="282.5" y2="439"/><rect class="actor" x="205" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="282.5" y="28" text-anchor="middle">On-prem DNS</text><line class="life" x1="457.5" y1="46" x2="457.5" y2="439"/><rect class="actor" x="380" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="457.5" y="28" text-anchor="middle">Private Resolver</text><line class="life" x1="632.5" y1="46" x2="632.5" y2="439"/><rect class="actor" x="555" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="632.5" y="28" text-anchor="middle">Private DNS zone</text><text class="lbl" x="195" y="84" text-anchor="middle">stdata.blob.core.windows.net?</text><text class="num" x="115.5" y="94" text-anchor="start">1</text><line class="arrow" x1="107.5" y1="99" x2="273.5" y2="99"/><polygon class="head" points="282.5,99 272.5,94 272.5,104"/><text class="lbl" x="370" y="134" text-anchor="middle">Conditional forwarder:</text><text class="lbl" x="370" y="149" text-anchor="middle">blob.core.windows.net</text><text class="num" x="290.5" y="159" text-anchor="start">2</text><line class="arrow" x1="282.5" y1="164" x2="448.5" y2="164"/><polygon class="head" points="457.5,164 447.5,159 447.5,169"/><rect class="note t-accent" x="332.5" y="187" width="250" height="44" rx="6"/><text class="lbl" x="457.5" y="205" text-anchor="middle">Public CNAME:</text><text class="lbl" x="457.5" y="220" text-anchor="middle">stdata.privatelink.blob…</text><text class="lbl" x="545" y="278" text-anchor="middle">Look up the privatelink</text><text class="lbl" x="545" y="293" text-anchor="middle">name</text><text class="num" x="465.5" y="303" text-anchor="start">4</text><line class="arrow" x1="457.5" y1="308" x2="623.5" y2="308"/><polygon class="head" points="632.5,308 622.5,303 622.5,313"/><text class="lbl" x="545" y="343" text-anchor="middle">A record: 10.20.1.5</text><text class="num" x="624.5" y="353" text-anchor="end">5</text><line class="arrow t-ok" x1="632.5" y1="358" x2="466.5" y2="358"/><polygon class="head t-ok" points="457.5,358 467.5,353 467.5,363"/><text class="lbl" x="282.5" y="393" text-anchor="middle">10.20.1.5: traffic stays private</text><text class="num" x="449.5" y="403" text-anchor="end">6</text><line class="arrow t-ok" x1="457.5" y1="408" x2="116.5" y2="408"/><polygon class="head t-ok" points="107.5,408 117.5,403 117.5,413"/></svg></div><figcaption>Resolving a storage account with a private endpoint from on-premises.</figcaption></figure>

<figure class="flow"><ol class="steps"><li><div class="node"><small>01</small>Client looks up stdata.blob.core.windows.net</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>02</small>CNAME to privatelink name</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>03</small>Private DNS zone answers 10.20.1.5</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>04</small>Traffic stays on your network</div></li></ol><figcaption>If the lookup returns a public IP, DNS is the problem.</figcaption></figure>

<h2 id="how-it-should-resolve">How it should resolve</h2>
<p>When a private endpoint exists, the public name becomes a CNAME to a <code>privatelink</code> name:</p>
<div class="codebox"><div class="codebar"><span>text</span><button type="button" data-copy="" data-label="copy">copy</button></div><pre><code>stdata.blob.core.windows.net
  → stdata.privatelink.blob.core.windows.net
    → 10.20.1.5   (inside your network)
    → public IP   (everywhere else)</code></pre></div>
<p>Your network needs to answer the <code>privatelink</code> name with the private IP. That's the job of a <b>private DNS zone</b>, such as <code>privatelink.blob.core.windows.net</code>.</p>

<h2 id="inside-azure">Inside Azure</h2>
<ul>
  <li>Create the private DNS zone. Usually the private endpoint wizard does this for you.</li>
  <li><b>Link it to every virtual network</b> that needs to resolve it. In hub-and-spoke, link it to the hub, and make sure spokes use DNS that can resolve through the hub.</li>
  <li>Keep <b>one zone per service type</b>, shared across the estate, rather than a new zone per endpoint.</li>
</ul>
<h3>Common zone names</h3>
<div class="tablewrap"><table>
  <tr><th>Service</th><th>Private DNS zone</th></tr>
  <tr><td>Blob storage</td><td><code>privatelink.blob.core.windows.net</code></td></tr>
  <tr><td>Azure Files</td><td><code>privatelink.file.core.windows.net</code></td></tr>
  <tr><td>Key Vault</td><td><code>privatelink.vaultcore.azure.net</code></td></tr>
  <tr><td>Azure SQL Database</td><td><code>privatelink.database.windows.net</code></td></tr>
  <tr><td>App Service and Functions</td><td><code>privatelink.azurewebsites.net</code></td></tr>
</table></div>
<p>Each sub-resource gets its own zone. A storage account with blob and file endpoints needs records in two zones.</p>

<h2 id="from-on-premises">From on-premises</h2>
<p>On-premises DNS servers can't query the Azure-provided resolver at <code>168.63.129.16</code> directly. That address only works from inside Azure. The usual fix:</p>
<ol>
  <li>Deploy an <b>Azure DNS Private Resolver</b> with an inbound endpoint in the hub.</li>
  <li>On on-premises DNS, create <b>conditional forwarders</b> for the public service domains (such as <code>blob.core.windows.net</code>) pointing to the inbound endpoint's IP.</li>
</ol>

<h2 id="troubleshooting">Troubleshooting</h2>
<div class="codebox"><div class="codebar"><span>shell</span><button type="button" data-copy="" data-label="copy">copy</button></div><pre><code>nslookup stdata.blob.core.windows.net</code></pre></div>
<p>Run it from the client that's failing. If you get a public IP back, DNS is the problem, not the private endpoint. Also check that the service's public network access setting matches your intent.</p>
<div class="callout"><strong>Scale tip:</strong> in a landing zone, use <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="azure-policy" data-term="Azure Policy" data-def="Rules that evaluate Azure resources and can audit, deny or automatically fix them, assigned at management group, subscription or resource group." data-post="/posts/azure-policy-allowed-locations-and-guardrails/">Azure Policy</span> to create the DNS records in the central private DNS zones automatically whenever a private endpoint is deployed.</div>
<figure class="dg dg-decision-wrap"><div class="dg-decision"><div class="dg-q">Resolve the public name from the client. What comes back?</div><div class="dg-branches"><div class="dg-branch t-ok"><span class="dg-if">A private IP</span><span class="dg-then">DNS is right. Look at NSGs, routing or firewalls</span></div><div class="dg-branch t-bad"><span class="dg-if">A public IP via the privatelink CNAME</span><span class="dg-then">The client's DNS can't see the private zone. Check forwarding and VNet links</span></div><div class="dg-branch t-accent"><span class="dg-if">A public IP with no privatelink CNAME</span><span class="dg-then">No private endpoint on that sub-resource, or the wrong name</span></div><div class="dg-branch t-bad"><span class="dg-if">NXDOMAIN</span><span class="dg-then">The privatelink zone exists but has no record for this resource</span></div></div></div><figcaption>What nslookup tells you.</figcaption></figure>
<div class="codebox"><div class="codebar"><span>powershell</span><button type="button" data-copy="" data-label="copy">copy</button></div><pre><code># PowerShell equivalent, showing the CNAME chain
Resolve-DnsName stdata.blob.core.windows.net | Format-Table Name, Type, IPAddress, NameHost</code></pre></div>
<ul class="check"><li>Forward the public service domain, not the privatelink one, from on-premises</li><li>Avoid hosting privatelink zones on on-premises DNS: any account without a record there stops resolving</li><li>Custom DNS servers in Azure must forward to 168.63.129.16</li><li>Link private zones to the VNet your DNS servers or resolver live in</li><li>Use Azure Policy to create DNS records when private endpoints are created</li></ul>

]]></content:encoded>
</item>
<item>
<title>Key Vault: move from access policies to Azure RBAC</title>
<link>https://azureblog.co.uk/posts/key-vault-rbac-vs-access-policies/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/key-vault-rbac-vs-access-policies/</guid>
<pubDate>Wed, 12 Aug 2026 08:00:00 GMT</pubDate>
<category>Azure</category><category>Security</category>
<description>Key Vault has two permission models. Azure RBAC is the recommended one, and switching is simpler than it looks if you plan the role mapping first.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/key-vault-rbac-vs-access-policies.png" alt=""></p><p><span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="key-vault" data-term="Key Vault" data-def="Azure&#39;s service for storing secrets, keys and certificates, with access controlled by Azure RBAC or legacy access policies.">Key Vault</span> can control data access with <b>vault access policies</b> (the original model) or <b>Azure RBAC</b>. Microsoft recommends <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="rbac" data-term="RBAC" data-def="Role-based access control: permissions granted by assigning roles to users, groups or identities at a scope.">RBAC</span>, and there are good reasons.</p>
<figure class="dg dg-compare-wrap"><div class="dg-compare" style="--cols:2"><div class="dg-col t-bad"><h4>Vault access policies</h4><p class="dg-sub">Legacy model</p><ul><li>Set on the vault itself</li><li>Whole-vault scope only</li><li>Vault managers can grant themselves data access</li><li>No PIM</li></ul></div><div class="dg-col t-ok"><h4>Azure RBAC</h4><p class="dg-sub">Recommended</p><ul><li>Same model as all Azure resources</li><li>Scope to vault or a single secret</li><li>Separates management from data access</li><li>Works with PIM and access reviews</li></ul></div></div><figcaption>The two permission models for Key Vault data.</figcaption></figure>

<h2 id="why-rbac-is-better">Why RBAC is better</h2>
<ul>
  <li><b>One model everywhere.</b> Permissions are managed the same way as every other Azure resource, and they appear in the same reviews and reports.</li>
  <li><b>PIM support.</b> Data-plane roles can be made eligible and time-boxed.</li>
  <li><b>Finer scope.</b> Roles can be assigned on a single secret, key or certificate, not just the whole vault.</li>
  <li><b>Separation of duties.</b> With access policies, anyone who can manage the vault can grant themselves access to its secrets. With RBAC, data access needs a role assignment permission they may not have.</li>
</ul>

<h2 id="common-role-mappings">Common role mappings</h2>
<div class="tablewrap"><table>
  <tr><th>Role</th><th>Use for</th></tr>
  <tr><td>Key Vault Secrets User</td><td>Apps that read secrets</td></tr>
  <tr><td>Key Vault Crypto User</td><td>Apps that use keys to encrypt, decrypt, sign or verify</td></tr>
  <tr><td>Key Vault Certificate User</td><td>Apps that read certificates</td></tr>
  <tr><td>Key Vault Secrets Officer</td><td>People who manage secrets</td></tr>
  <tr><td>Key Vault Administrator</td><td>Full data-plane management. Use sparingly, ideally through <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="pim" data-term="PIM" data-def="Privileged Identity Management: users are made eligible for admin roles and activate them only when needed, for a limited time, with justification and checks." data-post="/posts/activate-pim-roles-with-powershell/">PIM</span>.</td></tr>
</table></div>
<figure class="dg dg-decision-wrap"><div class="dg-decision"><div class="dg-q">What does it do with the vault?</div><div class="dg-branches"><div class="dg-branch t-ok"><span class="dg-if">App reads secrets</span><span class="dg-then">Key Vault Secrets User</span></div><div class="dg-branch t-ok"><span class="dg-if">App encrypts or signs with keys</span><span class="dg-then">Key Vault Crypto User</span></div><div class="dg-branch t-accent"><span class="dg-if">Person rotates secrets</span><span class="dg-then">Key Vault Secrets Officer, ideally eligible through PIM</span></div><div class="dg-branch"><span class="dg-if">Manages vault settings only</span><span class="dg-then">Key Vault Contributor (no data access under RBAC)</span></div></div></div><figcaption>Which role does this identity need?</figcaption></figure>

<h2 id="switching-over">Switching over</h2>
<ol>
  <li>List the existing access policies and map each to an RBAC role.</li>
  <li>Create the role assignments <b>before</b> switching. Role assignments can take a few minutes to take effect.</li>
  <li>Switch the permission model:</li>
</ol>
<div class="codebox"><div class="codebar"><span>shell</span><button type="button" data-copy="" data-label="copy">copy</button></div><pre><code>az keyvault update --name kv-app-prod --resource-group rg-app --enable-rbac-authorization true</code></pre></div>
<ol start="4">
  <li>Test every app that uses the vault. If something breaks, switching back restores the old access policies.</li>
</ol>
<div class="callout"><strong>Note:</strong> changing the permission model is itself a sensitive operation. Restrict who can do it, and treat it as a change, not a tweak.</div>
<h2 id="finding-vaults-still-on-access-policies">Finding vaults still on access policies</h2>
<div class="codebox"><div class="codebar"><span>kql</span><button type="button" data-copy="" data-label="copy">copy</button></div><pre><code>// Azure Resource Graph
resources
| where type == "microsoft.keyvault/vaults"
| extend rbac = tobool(properties.enableRbacAuthorization)
| where rbac != true
| project name, resourceGroup, subscriptionId</code></pre></div>
<p>Pair this with the built-in <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="azure-policy" data-term="Azure Policy" data-def="Rules that evaluate Azure resources and can audit, deny or automatically fix them, assigned at management group, subscription or resource group." data-post="/posts/azure-policy-allowed-locations-and-guardrails/">Azure Policy</span> that audits Key Vaults not using RBAC, so new vaults don't add to the list.</p>
<ul class="check"><li>Map every access policy to a role</li><li>Create role assignments and wait for them to apply</li><li>Switch the permission model</li><li>Test each app and pipeline</li><li>Remove leftover Contributor rights that were only there for access policies</li></ul>
<details class="faq"><summary>Does switching delete the access policies?</summary><p>They're kept but ignored while RBAC is on. Switching back makes them active again, which is your rollback.</p></details>
<details class="faq"><summary>Who can make the switch?</summary><p>Changing the permission model needs permission to create role assignments, such as Owner or User Access Administrator, because it changes who can reach the data.</p></details>

]]></content:encoded>
</item>
<item>
<title>Intune in July: macOS custom compliance and Defender settings that finally win</title>
<link>https://azureblog.co.uk/posts/intune-july-2026-roundup/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/intune-july-2026-roundup/</guid>
<pubDate>Wed, 05 Aug 2026 08:00:00 GMT</pubDate>
<category>Intune</category><category>Security</category><category>What&#39;s new</category>
<description>Custom compliance comes to macOS, a preview stops Group Policy overriding your Defender settings, and Store app search goes regional.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/intune-july-2026-roundup.png" alt=""></p><h2 id="custom-compliance-for-macos">Custom compliance for macOS</h2>
<p>Custom compliance has been available for Windows and Linux: a script reports values and a JSON file defines the rules. It now comes to macOS. If you've been unable to express a Mac compliance requirement with the built-in settings, such as a specific agent running or a configuration file present, you now can.</p>
<p>The pattern is the same on every platform: a script reports values as JSON, and a rules file states what's compliant.</p>
<figure class="dg dg-seq-wrap"><div class="dg-scroll"><svg class="dg-seq" viewBox="0 0 670 382" width="670" height="382" role="img" aria-label="Sequence diagram: Intune, Device, Discovery script"><line class="life" x1="125" y1="46" x2="125" y2="374"/><rect class="actor" x="30" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="125" y="28" text-anchor="middle">Intune</text><line class="life" x1="335" y1="46" x2="335" y2="374"/><rect class="actor" x="240" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="335" y="28" text-anchor="middle">Device</text><line class="life" x1="545" y1="46" x2="545" y2="374"/><rect class="actor" x="450" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="545" y="28" text-anchor="middle">Discovery script</text><text class="lbl" x="230" y="84" text-anchor="middle">Delivers script and JSON</text><text class="lbl" x="230" y="99" text-anchor="middle">rules</text><text class="num" x="133" y="109" text-anchor="start">1</text><line class="arrow" x1="125" y1="114" x2="326" y2="114"/><polygon class="head" points="335,114 325,109 325,119"/><text class="lbl" x="440" y="149" text-anchor="middle">Runs script</text><text class="num" x="343" y="159" text-anchor="start">2</text><line class="arrow" x1="335" y1="164" x2="536" y2="164"/><polygon class="head" points="545,164 535,159 535,169"/><text class="lbl" x="440" y="199" text-anchor="middle">Outputs values as JSON</text><text class="num" x="537" y="209" text-anchor="end">3</text><line class="arrow" x1="545" y1="214" x2="344" y2="214"/><polygon class="head" points="335,214 345,209 345,219"/><rect class="note" x="210" y="237" width="250" height="29" rx="6"/><text class="lbl" x="335" y="255" text-anchor="middle">Compares values with rules</text><text class="lbl" x="230" y="313" text-anchor="middle">Compliant or not, with your</text><text class="lbl" x="230" y="328" text-anchor="middle">custom message</text><text class="num" x="327" y="338" text-anchor="end">5</text><line class="arrow t-ok" x1="335" y1="343" x2="134" y2="343"/><polygon class="head t-ok" points="125,343 135,338 135,348"/></svg></div><figcaption>How custom compliance evaluates a device.</figcaption></figure>

<h2 id="controlled-configuration-for-defender-antivirus-preview">Controlled configuration for Defender Antivirus (preview)</h2>
<p>Defender Antivirus settings can come from Intune, Defender for Endpoint, Group Policy, Configuration Manager or local scripts, and conflicts are a classic headache. The new <b>controlled configuration</b> preview lets Intune or Defender for Endpoint settings take precedence over Group Policy, Configuration Manager and local scripts. For co-managed estates mid-migration, that's very welcome.</p>
<figure class="dg dg-layers-wrap"><div class="dg-layers"><div class="dg-layer" style="--depth:0"><div class="dg-lh"><b>Intune or Defender for Endpoint</b><span>Takes precedence when controlled configuration is on</span></div><div class="dg-layer" style="--depth:1"><div class="dg-lh"><b>Group Policy / Configuration Manager</b><span>Overridden</span></div><div class="dg-layer" style="--depth:2"><div class="dg-lh"><b>Local scripts and settings</b><span>Overridden</span></div></div></div></div></div><figcaption>Where Defender settings can come from, and which wins with controlled configuration.</figcaption></figure>

<h2 id="samsung-knox-e-fota">Samsung Knox E-FOTA</h2>
<p>Intune can now manage firmware updates for corporate-owned Samsung devices with Knox E-FOTA, across dedicated, fully managed and work profile corporate-owned devices.</p>

<h2 id="regional-microsoft-store-search">Regional Microsoft Store search</h2>
<p>When adding Microsoft Store apps, you can now choose a market catalogue rather than only searching the US catalogue. Useful when an app is only published in certain regions.</p>
<h2 id="what-to-do-this-month">What to do this month</h2>
<ul class="check"><li>Mac fleets: list compliance requirements the built-in settings can't express, and pilot custom compliance</li><li>Co-managed fleets: test controlled configuration in a pilot to end Defender setting conflicts</li><li>Samsung corporate devices: decide whether to manage firmware updates with E-FOTA</li><li>Check your Store app search uses the right market catalogue</li></ul>

<div class="sources"><b>Sources</b><ul><li><a href="https://learn.microsoft.com/en-us/intune/whats-new/" target="_blank" rel="noopener">Microsoft Learn: What's new in Microsoft Intune</a></li></ul></div>
]]></content:encoded>
</item>
<item>
<title>Windows LAPS with Intune: unique local admin passwords in minutes</title>
<link>https://azureblog.co.uk/posts/windows-laps-with-intune/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/windows-laps-with-intune/</guid>
<pubDate>Wed, 29 Jul 2026 08:00:00 GMT</pubDate>
<category>Intune</category><category>Windows</category><category>Security</category>
<description>If every PC shares the same local administrator password, one compromised machine means all of them. Windows LAPS fixes that, and it&#39;s built into Windows and Intune.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/windows-laps-with-intune.png" alt=""></p><p>A shared local admin password is a gift to attackers: compromise one device, extract the password hash, and move laterally to every other device with the same password. <b>Windows LAPS</b> gives every device its own random local admin password, rotates it automatically and backs it up to Entra ID.</p>
<figure class="dg dg-compare-wrap"><div class="dg-compare" style="--cols:2"><div class="dg-col t-bad"><h4>Shared local admin password</h4><ul><li>Same password on every PC</li><li>One compromise opens them all</li><li>Rarely changed</li></ul></div><div class="dg-col t-ok"><h4>Windows LAPS</h4><ul><li>Unique random password per device</li><li>Rotated automatically</li><li>Backed up to Entra ID with audited access</li></ul></div></div><figcaption>Before and after Windows LAPS.</figcaption></figure>

<figure class="flow"><ol class="steps"><li><div class="node"><small>01</small>Enable LAPS in Entra</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>02</small>Intune LAPS policy</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>03</small>Device rotates password</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>04</small>Backed up to Entra ID</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>05</small>Audited retrieval</div></li></ol></figure>

<h2 id="1-enable-laps-in-entra">1. Enable LAPS in Entra</h2>
<p>In the Entra admin center, go to <b>Devices → Device settings</b> and set <b>Enable Microsoft Entra Local Administrator Password Solution (LAPS)</b> to Yes. Without this, devices can't back up their passwords.</p>

<h2 id="2-create-the-intune-policy">2. Create the Intune policy</h2>
<p>In Intune, go to <b>Endpoint security → Account protection</b> and create a <b>Local admin password solution (Windows LAPS)</b> policy:</p>
<ul>
  <li><b>Backup directory:</b> Backup the password to Azure AD only (Entra ID).</li>
  <li><b>Password age:</b> such as 30 days.</li>
  <li><b>Password complexity and length:</b> large letters, small letters, numbers and specials, at least 14 characters.</li>
  <li><b>Post-authentication actions:</b> reset the password, and optionally log off, after the password is used. This stops a retrieved password staying valid.</li>
</ul>
<p>If you've renamed the built-in Administrator account or use a custom one, set <b>Administrator account name</b> to match.</p>

<h2 id="3-retrieve-a-password">3. Retrieve a password</h2>
<p>In Intune, open the device and choose <b>Local admin password</b>. Retrievals are audited. Give the permission only to roles that need it, such as a custom role for the service desk.</p>
<figure class="dg dg-seq-wrap"><div class="dg-scroll"><svg class="dg-seq" viewBox="0 0 670 381" width="670" height="381" role="img" aria-label="Sequence diagram: Technician, Intune / Entra, Device"><line class="life" x1="125" y1="46" x2="125" y2="373"/><rect class="actor" x="30" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="125" y="28" text-anchor="middle">Technician</text><line class="life" x1="335" y1="46" x2="335" y2="373"/><rect class="actor" x="240" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="335" y="28" text-anchor="middle">Intune / Entra</text><line class="life" x1="545" y1="46" x2="545" y2="373"/><rect class="actor" x="450" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="545" y="28" text-anchor="middle">Device</text><text class="lbl" x="230" y="84" text-anchor="middle">Views local admin password</text><text class="num" x="133" y="94" text-anchor="start">1</text><line class="arrow" x1="125" y1="99" x2="326" y2="99"/><polygon class="head" points="335,99 325,94 325,104"/><rect class="note t-accent" x="210" y="122" width="250" height="29" rx="6"/><text class="lbl" x="335" y="140" text-anchor="middle">Retrieval audited</text><text class="lbl" x="335" y="198" text-anchor="middle">Uses password to sign in locally</text><text class="num" x="133" y="208" text-anchor="start">3</text><line class="arrow" x1="125" y1="213" x2="536" y2="213"/><polygon class="head" points="545,213 535,208 535,218"/><rect class="note t-ok" x="416" y="236" width="250" height="44" rx="6"/><text class="lbl" x="541" y="254" text-anchor="middle">Post-authentication action:</text><text class="lbl" x="541" y="269" text-anchor="middle">reset password</text><text class="lbl" x="440" y="327" text-anchor="middle">New password backed up</text><text class="num" x="537" y="337" text-anchor="end">5</text><line class="arrow t-ok" x1="545" y1="342" x2="344" y2="342"/><polygon class="head t-ok" points="335,342 345,337 345,347"/></svg></div><figcaption>A service desk retrieval, start to finish.</figcaption></figure>

<h2 id="4-rotate-on-demand">4. Rotate on demand</h2>
<p>Use the <b>Rotate local admin password</b> remote action after a password has been shared during a support call.</p>
<div class="callout"><strong>Don't forget the legacy client:</strong> if devices still run the old Microsoft <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="laps" data-term="Windows LAPS" data-def="Local Administrator Password Solution: Windows sets a unique, rotating local admin password on each device and backs it up to Entra ID or AD.">LAPS</span> (the separate MSI), plan to remove it. Windows LAPS is built into supported Windows versions.</div>
<h2 id="checking-it-s-working">Checking it's working</h2>
<ul>
  <li>In Intune, the device's <b>Local admin password</b> page shows when the password was last rotated.</li>
  <li>On the device, Event Viewer has a dedicated log at <b>Applications and Services Logs → Microsoft → Windows → LAPS</b>.</li>
  <li>In PowerShell on the device, <code>Get-LapsDiagnostics</code> collects logs for troubleshooting.</li>
</ul>
<h2 id="common-problems">Common problems</h2>
<ul>
  <li><b>No password shown:</b> check the Entra device setting for LAPS is enabled. Without it, the backup is rejected.</li>
  <li><b>Wrong account managed:</b> if you renamed the built-in Administrator, set the account name in the policy.</li>
  <li><b>Conflicts:</b> remove old LAPS Group Policy settings or the legacy LAPS client to avoid two policies fighting.</li>
</ul>

]]></content:encoded>
</item>
<item>
<title>Microsoft Authenticator now blocks jailbroken and rooted devices</title>
<link>https://azureblog.co.uk/posts/authenticator-jailbreak-root-detection/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/authenticator-jailbreak-root-detection/</guid>
<pubDate>Wed, 22 Jul 2026 08:00:00 GMT</pubDate>
<category>Entra ID</category><category>Security</category><category>What&#39;s new</category>
<description>Authenticator now refuses to add or use work and school accounts on jailbroken or rooted phones. There&#39;s nothing to configure, but your service desk should know.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/authenticator-jailbreak-root-detection.png" alt=""></p><p>Jailbreak and root detection in Microsoft Authenticator is now generally available. On a jailbroken iPhone or rooted Android device, Authenticator blocks users from adding work or school accounts, and from using existing ones.</p>
<figure class="dg dg-decision-wrap"><div class="dg-decision"><div class="dg-q">Is the phone jailbroken or rooted?</div><div class="dg-branches"><div class="dg-branch t-ok"><span class="dg-if">No</span><span class="dg-then">Work accounts work as normal.</span></div><div class="dg-branch t-bad"><span class="dg-if">Yes</span><span class="dg-then">Authenticator blocks adding or using work and school accounts.</span></div></div></div><figcaption>What happens when a user opens Authenticator.</figcaption></figure>

<h2 id="why-it-matters">Why it matters</h2>
<p>A jailbroken or rooted phone has its platform protections removed. Malware on such a device can do much more, including interfering with the app that's supposed to prove who the user is. Blocking these devices protects the integrity of <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="mfa" data-term="MFA" data-def="Multifactor authentication: proving who you are with more than one factor, such as something you know, something you have, or something you are.">MFA</span> and <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="passkey" data-term="Passkey" data-def="A FIDO2 credential made of a key pair. The private key stays on the device or in a password manager and only signs in to the site it was created for." data-post="/posts/synced-passkeys-and-passkey-profiles/">passkeys</span> held in Authenticator.</p>

<h2 id="what-you-need-to-do">What you need to do</h2>
<ul>
  <li><b>No configuration.</b> It's enforced automatically and can't be switched off.</li>
  <li><b>Brief the service desk.</b> A user who suddenly can't use Authenticator may be on a modified device. The fix is a supported device or another method, not a workaround.</li>
  <li><b>Check for single points of failure.</b> Users whose only method is Authenticator on a blocked device can't complete MFA. A <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="tap" data-term="Temporary Access Pass" data-def="A time-limited passcode issued by an admin, used to sign in and register passwordless methods without ever having a password." data-post="/posts/temporary-access-pass-onboarding/">temporary access pass</span> gets them going while they register something else.</li>
</ul>
<h2 id="why-modified-phones-are-risky">Why modified phones are risky</h2>
<p>Jailbreaking an iPhone or rooting an Android phone removes protections the platform relies on: app sandboxing, secure storage and checks that apps haven't been tampered with. On such a device, malware can read data from other apps, intercept what's on screen, or interfere with the app that proves who the user is. An authenticator on that phone can no longer be trusted to be what it says it is.</p>
<h2 id="helping-affected-users">Helping affected users</h2>
<ol>
  <li>Confirm the issue: the user will see Authenticator refuse to add or use their work account.</li>
  <li>Issue a <a href="https://azureblog.co.uk/posts/temporary-access-pass-onboarding/">Temporary Access Pass</a> so they can sign in and register a different method.</li>
  <li>Offer an alternative: a supported phone, a <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="fido2" data-term="FIDO2" data-def="The open standard behind passkeys and security keys, combining WebAuthn in the browser with CTAP for authenticators.">FIDO2</span> security key, or <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="whfb" data-term="Windows Hello for Business" data-def="Phishing-resistant sign-in to Windows using a PIN or biometrics, backed by a key held in the device&#39;s TPM.">Windows Hello for Business</span> on their work PC.</li>
  <li>If the user restores their phone to a supported state, they can set Authenticator up again.</li>
</ol>
<h2 id="check-the-bigger-picture">Check the bigger picture</h2>
<p>If you use Intune, device <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="compliance-policy" data-term="Compliance policy" data-def="Intune rules a device must meet, such as encryption or a minimum OS version. Conditional Access can require a compliant device." data-post="/posts/rolling-out-require-compliant-device/">compliance policies</span> can also mark jailbroken or rooted devices as not compliant, which blocks access through <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="conditional-access" data-term="Conditional Access" data-def="Entra ID&#39;s policy engine. Each policy says: if these users sign in to these apps under these conditions, then require (or block) something, such as MFA or a compliant device." data-post="/posts/conditional-access-report-only-and-what-if/">Conditional Access</span>. Together, the two cover both the authenticator and the corporate apps.</p>

<div class="sources"><b>Sources</b><ul><li><a href="https://learn.microsoft.com/en-us/entra/fundamentals/whats-new" target="_blank" rel="noopener">Microsoft Learn: What's new in Microsoft Entra</a></li></ul></div>
]]></content:encoded>
</item>
<item>
<title>BYOD Windows access with Entra registration is now GA</title>
<link>https://azureblog.co.uk/posts/byod-windows-entra-registration-ga/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/byod-windows-entra-registration-ga/</guid>
<pubDate>Wed, 15 Jul 2026 08:00:00 GMT</pubDate>
<category>Entra ID</category><category>Windows</category><category>What&#39;s new</category>
<description>Users, partners and internal guests can now reach corporate resources from personal Windows devices using Entra registration, without joining them to anything.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/byod-windows-entra-registration-ga.png" alt=""></p><p>Personal Windows PCs have always been awkward. You don't want to join them to your directory, but users still need a supported way to reach email, Teams and web apps. Support for BYOD Windows devices using <b>Entra registration</b> is now generally available. It covers users, partners and internal guests.</p>
<figure class="dg dg-compare-wrap"><div class="dg-compare" style="--cols:3"><div class="dg-col t-accent"><h4>Entra registered</h4><p class="dg-sub">personal devices</p><ul><li>User adds a work account</li><li>User owns and controls the device</li><li>Light-touch identity for Conditional Access</li></ul></div><div class="dg-col t-ok"><h4>Entra joined</h4><p class="dg-sub">corporate cloud devices</p><ul><li>Organisation owns the device</li><li>Sign in with work account</li><li>Fully managed with Intune</li></ul></div><div class="dg-col"><h4>Hybrid joined</h4><p class="dg-sub">corporate AD devices</p><ul><li>Joined to on-premises AD</li><li>Also registered in Entra</li><li>Managed by GPO and/or Intune</li></ul></div></div><figcaption>The three ways a Windows device relates to Entra ID.</figcaption></figure>

<h2 id="registered-not-joined">Registered, not joined</h2>
<ul>
  <li><b>Entra joined</b> devices are corporate machines whose identity belongs to your organisation.</li>
  <li><b>Entra registered</b> devices are personal machines where the user adds a work account. The device gets an identity in your tenant, but the user keeps control of it.</li>
</ul>

<h2 id="controlling-what-personal-devices-can-do">Controlling what personal devices can do</h2>
<p>Registration on its own isn't a security control. Pair it with <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="conditional-access" data-term="Conditional Access" data-def="Entra ID&#39;s policy engine. Each policy says: if these users sign in to these apps under these conditions, then require (or block) something, such as MFA or a compliant device." data-post="/posts/conditional-access-report-only-and-what-if/">Conditional Access</span>:</p>
<ul>
  <li>Allow browser-only access to sensitive apps from unmanaged devices, using app-enforced restrictions in SharePoint and Exchange to block downloads.</li>
  <li>Require <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="phishing-resistant" data-term="Phishing-resistant MFA" data-def="Sign-in methods bound to the real site, so they can&#39;t be relayed by a fake one: passkeys (FIDO2), Windows Hello for Business and multifactor certificate-based authentication." data-post="/posts/authentication-strengths-explained/">phishing-resistant</span> <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="mfa" data-term="MFA" data-def="Multifactor authentication: proving who you are with more than one factor, such as something you know, something you have, or something you are.">MFA</span> from personal devices.</li>
  <li>Use Intune app protection policies, or Edge for Business protections, to keep corporate data inside managed apps.</li>
</ul>
<div class="callout"><strong>Plan for offboarding:</strong> when someone leaves, revoke their sessions and remove the registered device. Personal devices don't come back to IT.</div>
<h2 id="a-sensible-policy-set">A sensible policy set</h2>
<figure class="dg dg-decision-wrap"><div class="dg-decision"><div class="dg-q">What is the user trying to open?</div><div class="dg-branches"><div class="dg-branch t-ok"><span class="dg-if">Email and Teams in the browser</span><span class="dg-then">Allow, with phishing-resistant MFA</span></div><div class="dg-branch t-accent"><span class="dg-if">SharePoint files</span><span class="dg-then">Browser only, downloads blocked</span></div><div class="dg-branch t-bad"><span class="dg-if">Admin portals or sensitive apps</span><span class="dg-then">Block: managed devices only</span></div></div></div><figcaption>Access from a personal Windows device.</figcaption></figure>
<h2 id="registering-a-device">Registering a device</h2>
<ol>
  <li>On the personal PC, open <b>Settings → Accounts → Access work or school</b> and select <b>Connect</b>.</li>
  <li>Sign in with the work account and complete MFA.</li>
  <li>The device appears in Entra under the user's devices as registered.</li>
</ol>
<p>Make sure users know they can remove the work account from the same screen, and that IT can't see their personal files.</p>

<h2 id="questions-users-ask">Questions users ask</h2>
<details class="faq"><summary>Can IT see my personal files or browsing?</summary><p>No. Registration gives the device an identity for sign-in. Without Intune enrolment, IT can't see or manage your files, apps or settings.</p></details>
<details class="faq"><summary>Can IT wipe my PC?</summary><p>Not from registration alone. If app protection is used, IT can remove company data from managed apps, not your personal data.</p></details>
<details class="faq"><summary>How do I remove my work account?</summary><p>Settings → Accounts → Access work or school, select the account and choose Disconnect.</p></details>
<p>Putting these answers in your onboarding guide removes most of the hesitation users have about connecting a personal PC.</p>

<div class="sources"><b>Sources</b><ul><li><a href="https://learn.microsoft.com/en-us/entra/fundamentals/whats-new" target="_blank" rel="noopener">Microsoft Learn: What's new in Microsoft Entra</a></li></ul></div>
]]></content:encoded>
</item>
<item>
<title>Entra Connect Sync is on its way out. Start planning for Cloud Sync.</title>
<link>https://azureblog.co.uk/posts/connect-sync-to-cloud-sync/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/connect-sync-to-cloud-sync/</guid>
<pubDate>Wed, 08 Jul 2026 08:00:00 GMT</pubDate>
<category>Entra ID</category><category>Hybrid identity</category><category>What&#39;s new</category>
<description>Microsoft has started a phased move of identity sync from Entra Connect Sync to Cloud Sync, and has closed a privilege takeover route in hybrid sync. Here&#39;s what both changes mean for you.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/connect-sync-to-cloud-sync.png" alt=""></p><p>If you run hybrid identity, two changes this year deserve your attention. Microsoft is moving customers off Entra <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="connect-sync" data-term="Entra Connect Sync" data-def="The traditional server-based tool that syncs Active Directory objects to Entra ID, with configuration held on the server.">Connect Sync</span> for identity synchronisation, and it has blocked a technique attackers could use to take over privileged cloud accounts through sync.</p>
<figure class="flow"><ol class="steps"><li><div class="node"><small>01</small>Message Center notice</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>02</small>Inventory sync rules and features</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>03</small>Check the feature comparison</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>04</small>Pilot Cloud Sync</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>05</small>Transition identity sync</div></li></ol></figure>

<h2 id="the-move-to-cloud-sync">The move to Cloud Sync</h2>
<p>Entra <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="cloud-sync" data-term="Entra Cloud Sync" data-def="Microsoft&#39;s lightweight agent-based service for syncing users and groups from Active Directory to Entra ID, configured in the cloud.">Cloud Sync</span> replaces the heavy Connect Sync server with lightweight provisioning agents, with configuration held in the cloud. Microsoft is now transitioning customers to it in phases:</p>
<ul>
  <li><b>Notifications started in July 2026</b>, through the Message Center, Connect Health and targeted emails. Each tenant gets its own timeline.</li>
  <li><b>Simple tenants go first.</b> Early waves target organisations whose needs Cloud Sync already covers. Large directories and those using advanced Connect Sync features come later, as Cloud Sync gains capabilities.</li>
  <li><b>You can test first.</b> Microsoft provides guidance and a transition tool, and you can move and test sync in Cloud Sync before anything permanent changes.</li>
  <li><b>Hybrid authentication stays.</b> Features that let on-premises credentials reach cloud resources remain available through the Connect Sync configuration wizard. Cloud Sync takes over the identity sync job.</li>
</ul>
<figure class="dg dg-compare-wrap"><div class="dg-compare" style="--cols:2"><div class="dg-col"><h4>Entra Connect Sync</h4><p class="dg-sub">The traditional sync server</p><ul><li>Full server with a local SQL database</li><li>Configuration lives on the server</li><li>One active server, with staging mode for standby</li><li>Rich custom sync rules</li><li>Syncs devices for hybrid join</li></ul></div><div class="dg-col t-ok"><h4>Entra Cloud Sync</h4><p class="dg-sub">Lightweight agents</p><ul><li>Small provisioning agent on domain-joined servers</li><li>Configuration held in the cloud</li><li>Several agents for high availability</li><li>Works with disconnected forests</li><li>Gaining features over time</li></ul></div></div><figcaption>The two sync engines side by side.</figcaption></figure>

<h2 id="get-ready-before-your-notification-lands">Get ready before your notification lands</h2>
<ol>
  <li><b>Inventory your current setup.</b> List the custom sync rules, filtering, writeback options and other features you depend on.</li>
  <li><b>Check the feature comparison.</b> Microsoft publishes a comparison between Connect Sync and Cloud Sync. Anything you rely on that Cloud Sync lacks probably puts you in a later wave.</li>
  <li><b>Read the migration guide</b> and try Cloud Sync in a test environment or on a pilot OU.</li>
  <li><b>Look at Source of Authority.</b> Moving the <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="soa" data-term="Source of Authority" data-def="Which directory owns an object&#39;s attributes. Converting a synced user&#39;s source of authority makes it managed in the cloud instead of in AD." data-post="/posts/convert-synced-users-to-cloud-source-of-authority/">source of authority</span> for users or groups from AD to the cloud is a separate option, and you don't have to migrate sync first. For groups that no longer need to live in AD, it's worth considering.</li>
</ol>
<figure class="dg dg-timeline-wrap"><ol class="dg-tl"><li class=""><span class="dg-dot"></span><time>Now</time><span>Inventory sync rules, filters, writeback and device sync</span></li><li class="t-accent"><span class="dg-dot"></span><time>Next</time><span>Compare against the Cloud Sync feature list and note any gaps</span></li><li class="t-accent"><span class="dg-dot"></span><time>Pilot</time><span>Install agents and test Cloud Sync on a scoped OU</span></li><li class=""><span class="dg-dot"></span><time>Notification</time><span>Follow your tenant's timeline from Microsoft</span></li><li class="t-ok"><span class="dg-dot"></span><time>Cut over</time><span>Move identity sync, keep hybrid authentication features</span></li></ol><figcaption>A sensible order of work for most tenants.</figcaption></figure>
<h2 id="running-both-side-by-side">Running both side by side</h2>
<p>Connect Sync and Cloud Sync can run in the same tenant as long as they sync different objects. That's how most pilots work: scope Cloud Sync to a test OU, and exclude the same OU from Connect Sync. Never let both engines manage the same user, or they'll fight over its attributes.</p>
<h2 id="questions-to-answer-in-your-inventory">Questions to answer in your inventory</h2>
<ul class="check"><li>Which OUs and groups are in scope, and is any attribute filtering used?</li><li>Are there custom sync rules, and what do they do?</li><li>Is password writeback, group writeback or device writeback in use?</li><li>Are computer objects synced for <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="hybrid-join" data-term="Hybrid join" data-def="A Windows device joined to on-premises Active Directory and also registered in Entra ID, so it can use device-based Conditional Access." data-post="/posts/hybrid-join-with-entra-kerberos-preview/">hybrid join</span>?</li><li>Which authentication method: password hash sync, pass-through or federation?</li><li>Is Exchange hybrid in use, with its attribute writeback?</li></ul>

<h2 id="hard-match-is-now-blocked-for-privileged-users">Hard match is now blocked for privileged users</h2>
<p>When sync adds an AD object, Entra looks for an existing cloud object with a matching <code>OnPremisesImmutableId</code>. If it finds one, sync takes over that cloud object and overwrites it with the AD object's properties. This is a <b>hard match</b>.</p>
<p>That's useful when connecting existing cloud accounts to AD. It's also an attack path: someone with control over AD attributes could hard-match to a privileged cloud-only admin and take it over.</p>
<p>Since <b>1 June 2026</b>, Entra blocks sync from hard-matching a new AD user to a cloud user that holds an Entra role. Soft matching, hard matching for users without roles, and existing synced objects are unaffected.</p>
<div class="callout"><strong>If you hit it:</strong> sync reports an existing admin role conflict. That's by design. Remove the role from the cloud account, complete the match, then reassign the role, ideally as <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="pim" data-term="PIM" data-def="Privileged Identity Management: users are made eligible for admin roles and activate them only when needed, for a limited time, with justification and checks." data-post="/posts/activate-pim-roles-with-powershell/">PIM</span> eligible.</div>
<figure class="dg dg-decision-wrap"><div class="dg-decision"><div class="dg-q">Does the cloud account hold a privileged role?</div><div class="dg-branches"><div class="dg-branch t-ok"><span class="dg-if">No</span><span class="dg-then">Matching works as before, so sync can take over the account</span></div><div class="dg-branch t-bad"><span class="dg-if">Yes</span><span class="dg-then">The match is blocked. Sync can't take over the account</span></div><div class="dg-branch t-accent"><span class="dg-if">Legitimate need</span><span class="dg-then">Remove the role, complete the match, then reassign the role</span></div></div></div><figcaption>What happens when sync tries to match an on-premises user to a cloud account.</figcaption></figure>
<details class="faq"><summary>Do I have to move to Cloud Sync straight away?</summary><p>No. Microsoft is moving tenants in phases and will tell you when yours is due. Use the time to inventory and pilot so there are no surprises.</p></details>
<details class="faq"><summary>What about pass-through authentication and seamless SSO?</summary><p>Hybrid authentication features stay available through the Connect Sync configuration wizard. Cloud Sync takes over identity synchronisation.</p></details>
<details class="faq"><summary>Is device sync for hybrid join covered?</summary><p>Check the current feature comparison. If you rely on syncing computer objects, look at <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="entra-join" data-term="Entra join" data-def="A corporate Windows device joined directly to Entra ID, with no on-premises domain membership needed.">Entra join</span> for new devices, or the <a href="https://azureblog.co.uk/posts/hybrid-join-with-entra-kerberos-preview/">Entra Kerberos hybrid join preview</a>.</p></details>

<div class="sources"><b>Sources</b><ul>
  <li><a href="https://learn.microsoft.com/en-us/entra/fundamentals/whats-new" target="_blank" rel="noopener">Microsoft Learn: What's new in Microsoft Entra</a></li>
  <li><a href="https://learn.microsoft.com/en-us/entra/identity/hybrid/cloud-sync/migrate-azure-ad-connect-to-cloud-sync" target="_blank" rel="noopener">Microsoft Learn: Migrate from Connect Sync to Cloud Sync</a></li>
  <li><a href="https://learn.microsoft.com/en-us/entra/identity/hybrid/cloud-sync/connect-to-cloud-sync-decision-guide#comparison-between-microsoft-entra-connect-and-cloud-sync" target="_blank" rel="noopener">Microsoft Learn: Connect Sync and Cloud Sync comparison</a></li>
  <li><a href="https://learn.microsoft.com/en-us/entra/identity/hybrid/connect/tshoot-connect-sync-errors#existing-admin-role-conflict" target="_blank" rel="noopener">Microsoft Learn: Existing admin role conflict</a></li>
</ul></div>
]]></content:encoded>
</item>
<item>
<title>Intune&#39;s advanced features are coming to Microsoft 365 E3 and E5</title>
<link>https://azureblog.co.uk/posts/intune-suite-in-m365-e3-e5/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/intune-suite-in-m365-e3-e5/</guid>
<pubDate>Thu, 02 Jul 2026 08:00:00 GMT</pubDate>
<category>Intune</category><category>Microsoft 365</category><category>What&#39;s new</category>
<description>Remote Help, Endpoint Privilege Management, Cloud PKI and more are moving into the main Microsoft 365 licences. If you&#39;ve been holding off because of the add-on cost, it&#39;s time to look again.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/intune-suite-in-m365-e3-e5.png" alt=""></p><p>Many of Intune's best features have been paid add-ons, sold separately or in the Intune Suite. That made them easy to admire and hard to justify. Microsoft is now folding them into Microsoft 365 E3 and E5.</p>

<h2 id="what-you-get">What you get</h2>
<div class="tablewrap"><table>
  <tr><th>Licence</th><th>Adds</th></tr>
  <tr><td>M365 E3</td><td>Remote Help, Advanced Analytics, and Intune Plan 2 (Microsoft Tunnel for <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="mam" data-term="MAM" data-def="Mobile application management: protecting work data inside apps (copy, save and PIN rules) without managing the whole device.">MAM</span>, specialty device management, and firmware-over-the-air updates for supported devices)</td></tr>
  <tr><td>M365 E5</td><td>Everything in E3, plus Endpoint Privilege Management, Enterprise Application Management and Microsoft Cloud PKI</td></tr>
</table></div>
<figure class="dg dg-layers-wrap"><div class="dg-layers"><div class="dg-layer" style="--depth:0"><div class="dg-lh"><b>Microsoft 365 E5</b><span>Endpoint Privilege Management, Enterprise App Management, Cloud PKI</span></div><div class="dg-layer" style="--depth:1"><div class="dg-lh"><b>Microsoft 365 E3</b><span>Remote Help, Advanced Analytics, Intune Plan 2</span></div><div class="dg-layer" style="--depth:2"><div class="dg-lh"><b>Intune Plan 1</b><span>Core device and app management</span></div></div></div></div><p class="dg-down">↓ E5 includes everything in E3</p></div><figcaption>How the capabilities stack up.</figcaption></figure>

<h2 id="how-it-rolls-out">How it rolls out</h2>
<ul>
  <li>It's gradual, and eligible tenants are provisioned automatically. There's nothing to buy or assign.</li>
  <li>Microsoft posts a notice in the Microsoft 365 admin center <b>30 days</b> before the change reaches your tenant.</li>
  <li>It covers commercial E3 and E5 only. Education and frontline plans aren't changing for now, and government plans will follow, subject to compliance requirements.</li>
</ul>

<h2 id="what-to-do-with-it">What to do with it</h2>
<ul>
  <li><b>Endpoint Privilege Management (E5).</b> Lets standard users run specific approved tasks with elevation, without being local admins. If you still hand out local admin rights for one or two apps, this is the quickest security win on the list.</li>
  <li><b>Enterprise Application Management (E5).</b> A catalogue of pre-packaged <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="win32" data-term="Win32 app" data-def="A traditional Windows app packaged as an .intunewin file so Intune can install it with detection rules, dependencies and return codes." data-post="/posts/win32-app-packaging-detection-rules/">Win32 apps</span> that Intune can deploy and keep updated. Less packaging, fewer outdated apps.</li>
  <li><b>Cloud PKI (E5).</b> Issue certificates for Wi-Fi, VPN and other authentication from the cloud, without running your own certificate servers and connectors.</li>
  <li><b>Remote Help (E3).</b> Secure, Entra-authenticated remote support built into Intune, with role-based access and logging.</li>
  <li><b>Retire overlapping tools.</b> If you're paying for separate remote support, privilege management or PKI products, check renewal dates now.</li>
</ul>
<div class="callout"><strong>Watch for the notice:</strong> when the 30-day notice arrives, that's your cue to plan pilots. Having the licence doesn't configure anything, so nothing changes for users until you do.</div>
<h2 id="where-to-start">Where to start</h2>
<figure class="dg dg-decision-wrap"><div class="dg-decision"><div class="dg-q">What's your biggest pain point?</div><div class="dg-branches"><div class="dg-branch t-ok"><span class="dg-if">Users with local admin rights</span><span class="dg-then">Endpoint Privilege Management (E5)</span></div><div class="dg-branch t-ok"><span class="dg-if">Packaging and patching third-party apps</span><span class="dg-then">Enterprise App Management (E5)</span></div><div class="dg-branch t-ok"><span class="dg-if">Running certificate servers for Wi-Fi or VPN</span><span class="dg-then">Cloud PKI (E5)</span></div><div class="dg-branch t-accent"><span class="dg-if">Remote support tool costs</span><span class="dg-then">Remote Help (E3)</span></div></div></div><figcaption>Picking the first feature to adopt.</figcaption></figure>
<h2 id="an-endpoint-privilege-management-pilot">An Endpoint Privilege Management pilot</h2>
<ol>
  <li>List the reasons users currently need local admin, from your service desk tickets.</li>
  <li>Create elevation rules for the most common ones, such as a specific installer or tool.</li>
  <li>Pilot with a team that has admin rights today, then remove their local admin membership.</li>
  <li>Review the elevation reports weekly and add rules where requests repeat.</li>
</ol>

<div class="sources"><b>Sources</b><ul>
  <li><a href="https://learn.microsoft.com/en-us/intune/whats-new/" target="_blank" rel="noopener">Microsoft Learn: What's new in Microsoft Intune</a></li>
  <li><a href="https://learn.microsoft.com/en-us/intune/fundamentals/advanced-capabilities" target="_blank" rel="noopener">Microsoft Learn: Microsoft Intune advanced capabilities</a></li>
</ul></div>
]]></content:encoded>
</item>
<item>
<title>Entra Backup and Recovery is here: an undo button for your tenant</title>
<link>https://azureblog.co.uk/posts/entra-backup-and-recovery/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/entra-backup-and-recovery/</guid>
<pubDate>Wed, 24 Jun 2026 08:00:00 GMT</pubDate>
<category>Entra ID</category><category>What&#39;s new</category>
<description>Entra ID now keeps daily backups of your critical directory objects and lets you compare and roll back changes. It&#39;s on by default, but you need to know how it works before you need it.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/entra-backup-and-recovery.png" alt=""></p><p>For years, recovering from a bad change in Entra ID meant hoping the object was soft-deleted, or rebuilding it from documentation, exports or memory. A deleted <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="conditional-access" data-term="Conditional Access" data-def="Entra ID&#39;s policy engine. Each policy says: if these users sign in to these apps under these conditions, then require (or block) something, such as MFA or a compliant device." data-post="/posts/conditional-access-report-only-and-what-if/">Conditional Access</span> policy or a mangled set of group memberships could take hours to reconstruct. Entra Backup and Recovery, now generally available, changes that.</p>
<figure class="flow"><ol class="steps"><li><div class="node"><small>01</small>Daily backup, 7 days kept</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>02</small>Pick a snapshot</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>03</small>Create a difference report</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>04</small>Review every change</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>05</small>Recover objects</div></li></ol><figcaption>Always review the difference report before recovering.</figcaption></figure>

<h2 id="how-it-works">How it works</h2>
<ul>
  <li><b>Always on.</b> Entra takes one backup a day automatically. There's nothing to enable.</li>
  <li><b>Seven days of history.</b> Backups from the last seven days are kept, stored in the same geography as your tenant.</li>
  <li><b>Tamper-proof.</b> Nobody, including Global Administrators, can turn backups off, delete them or edit them. That matters if an attacker gets privileged access.</li>
  <li><b>Licensing.</b> It needs a workforce tenant with Entra ID P1 or P2. External ID and B2C tenants aren't supported.</li>
</ul>
<figure class="dg dg-timeline-wrap"><ol class="dg-tl"><li class=""><span class="dg-dot"></span><time>Day −7</time><span>Oldest backup still kept</span></li><li class="t-ok"><span class="dg-dot"></span><time>Day −3</time><span>Last known-good state</span></li><li class="t-bad"><span class="dg-dot"></span><time>Day −2</time><span>Conditional Access policy edited by mistake</span></li><li class=""><span class="dg-dot"></span><time>Day −1</time><span>Backup now includes the bad change</span></li><li class="t-accent"><span class="dg-dot"></span><time>Today</time><span>Difference report against day −3, then restore</span></li></ol><figcaption>Seven daily backups, always on. Pick the one from before the change.</figcaption></figure>

<h2 id="what-s-covered">What's covered</h2>
<p>Users, groups, applications, <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="service-principal" data-term="Service principal" data-def="An app&#39;s identity inside one tenant. An app registration is the global definition; the service principal is the local instance that gets permissions and signs in." data-post="/posts/managed-identities-vs-service-principals/">service principals</span>, Conditional Access policies, <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="named-location" data-term="Named location" data-def="A set of IP ranges or countries saved in Entra ID so Conditional Access policies can include or exclude them." data-post="/posts/named-locations-in-conditional-access/">named locations</span>, the authentication methods policy and selected authorization policy settings. Agent identities are covered too, because they're built from user and service principal objects.</p>
<p>It restores supported <b>properties and links</b> on objects that still exist. It doesn't bring back hard-deleted objects. For deleted users, groups and apps, the existing 30-day soft-delete recycle bin is still your first stop.</p>

<h2 id="recovering-from-a-bad-change">Recovering from a bad change</h2>
<ol>
  <li>In the Entra admin center, open <b>Backup and recovery</b>.</li>
  <li>Under <b>Backups</b>, pick a snapshot from before the change.</li>
  <li>Create a <b>Difference report</b>. It shows exactly which attributes and links differ from the current state.</li>
  <li>Review it carefully, then use <b>Recover objects</b> to restore everything, specific object types or specific object IDs.</li>
  <li>Track progress under <b>Recovery history</b>.</li>
</ol>
<div class="callout"><strong>Always review the difference report first.</strong> A recovery rolls back every change to the selected objects since that backup, including legitimate ones made by other admins.</div>
<figure class="dg dg-decision-wrap"><div class="dg-decision"><div class="dg-q">What went wrong?</div><div class="dg-branches"><div class="dg-branch t-ok"><span class="dg-if">Object deleted (user, group, app)</span><span class="dg-then">Restore from the 30-day recycle bin first</span></div><div class="dg-branch t-accent"><span class="dg-if">Object still exists but properties or links are wrong</span><span class="dg-then">Entra Backup and Recovery: difference report, then restore</span></div><div class="dg-branch t-bad"><span class="dg-if">Change older than seven days</span><span class="dg-then">Rebuild from your own exports or configuration as code</span></div></div></div><figcaption>Which recovery tool do you need?</figcaption></figure>

<h2 id="things-to-plan-for">Things to plan for</h2>
<ul>
  <li><b>Roles.</b> There are two new built-in roles: <b>Backup Reader</b> to view backups and differences, and <b>Backup Administrator</b> to run recoveries. Put the administrator role behind <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="pim" data-term="PIM" data-def="Privileged Identity Management: users are made eligible for admin roles and activate them only when needed, for a limited time, with justification and checks." data-post="/posts/activate-pim-roles-with-powershell/">PIM</span>.</li>
  <li><b>Hybrid objects.</b> For objects synced from Active Directory, you can produce difference reports but can't recover them in Entra. Fix those at the source in AD.</li>
  <li><b>Seven days is short.</b> If a bad change goes unnoticed for a week, it's beyond reach. Keep your audit log alerts for high-impact changes, like Conditional Access edits.</li>
  <li><b>Practise.</b> Make a harmless change in a test tenant and recover it, so the process isn't new on the day it matters.</li>
</ul>
<h2 id="practise-before-you-need-it">Practise before you need it</h2>
<ul class="check"><li>Run a difference report against yesterday's backup, just to see what normal change looks like</li><li>In a test tenant, change a Conditional Access policy and restore it</li><li>Decide who may restore, and require PIM activation for that role</li><li>Write the steps into your incident runbook</li><li>Keep your own exports of critical configuration for anything older than seven days</li></ul>
<h2 id="why-tamper-proof-matters">Why tamper-proof matters</h2>
<p>An attacker with Global Administrator can delete objects, weaken policies and cover their tracks. Backups they can't switch off or edit give you a known-good state to compare against, which is useful for investigation as well as recovery.</p>
<details class="faq"><summary>Does a restore overwrite everything?</summary><p>No. You choose what to restore after reviewing the difference report, so you can roll back one policy or a set of group memberships without touching anything else.</p></details>
<details class="faq"><summary>Can I extend retention beyond seven days?</summary><p>Not today. For longer history, keep your own snapshots, for example with <a href="https://azureblog.co.uk/posts/entra-tenant-configuration-management/">tenant configuration management</a> stored in Git.</p></details>

<div class="sources"><b>Sources</b><ul>
  <li><a href="https://learn.microsoft.com/en-us/entra/backup/overview" target="_blank" rel="noopener">Microsoft Learn: Entra Backup and Recovery overview</a></li>
  <li><a href="https://learn.microsoft.com/en-us/entra/backup/recover-objects" target="_blank" rel="noopener">Microsoft Learn: Recover objects</a></li>
  <li><a href="https://learn.microsoft.com/en-us/entra/fundamentals/whats-new" target="_blank" rel="noopener">Microsoft Learn: What's new in Microsoft Entra</a></li>
</ul></div>
]]></content:encoded>
</item>
<item>
<title>Account Discovery: find the accounts your SaaS apps forgot to tell you about</title>
<link>https://azureblog.co.uk/posts/entra-account-discovery-ga/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/entra-account-discovery-ga/</guid>
<pubDate>Wed, 17 Jun 2026 08:00:00 GMT</pubDate>
<category>Entra ID</category><category>Governance</category><category>What&#39;s new</category>
<description>Account Discovery reports the accounts that exist inside connected applications, including orphaned ones that nobody in Entra was ever assigned. It&#39;s now generally available.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/entra-account-discovery-ga.png" alt=""></p><p>Provisioning and <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="sso" data-term="SSO" data-def="Single sign-on: signing in once with your work account and getting into other apps without another password.">SSO</span> tell you who <i>should</i> have access to an app. They don't always tell you who <i>does</i>. Apps collect accounts over the years: created locally by an app admin, left behind after a migration, or belonging to people who left long ago.</p>
<figure class="dg dg-compare-wrap"><div class="dg-compare" style="--cols:3"><div class="dg-col t-ok"><h4>Assigned in Entra</h4><p class="dg-sub">Should have access</p><ul><li>Users and groups assigned to the app</li><li>Provisioned by Entra</li><li>Handled by joiner and leaver processes</li></ul></div><div class="dg-col t-accent"><h4>Discovered in the app</h4><p class="dg-sub">Actually has an account</p><ul><li>Every account the app reports</li><li>Includes local and legacy accounts</li><li>Matched to Entra users where possible</li></ul></div><div class="dg-col t-bad"><h4>Orphaned</h4><p class="dg-sub">The gap</p><ul><li>In the app, but no Entra assignment</li><li>Leavers, local admins, old migrations</li><li>Invisible to access reviews until now</li></ul></div></div><figcaption>What Entra knows versus what the app holds.</figcaption></figure>

<p><b>Account Discovery</b> reports the accounts that exist in your connected applications, and flags <b>orphaned accounts</b>: accounts in the app that don't correspond to an assignment in Entra. It's generally available and requires Entra ID Governance or Entra Suite licensing.</p>

<h2 id="using-it-well">Using it well</h2>
<ol>
  <li>Start with high-risk apps: finance, HR, anything holding client data, and any app with its own admin accounts.</li>
  <li>Review the orphaned accounts with the app owner. Some will be service or <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="break-glass" data-term="Break-glass account" data-def="An emergency cloud-only Global Administrator account, excluded from normal policies and closely monitored, used only when normal admin access fails." data-post="/posts/break-glass-accounts-done-right/">break-glass accounts</span> that are meant to be there; document those.</li>
  <li>Disable the rest in the app, and fix the process that created them.</li>
  <li>Bring legitimate users under Entra assignment so future joiners and leavers are handled automatically.</li>
</ol>
<div class="callout"><strong>Good for audits:</strong> "show me every account in this system and who owns it" is a common audit request. This answers it from one place.</div>
<figure class="dg dg-decision-wrap"><div class="dg-decision"><div class="dg-q">Who does this account belong to?</div><div class="dg-branches"><div class="dg-branch t-ok"><span class="dg-if">A current user</span><span class="dg-then">Assign them in Entra so the account is governed</span></div><div class="dg-branch t-accent"><span class="dg-if">A service or break-glass account</span><span class="dg-then">Document an owner and the reason, then review it yearly</span></div><div class="dg-branch t-bad"><span class="dg-if">A leaver or nobody</span><span class="dg-then">Disable in the app, then delete after a grace period</span></div></div></div><figcaption>Working through an orphaned account.</figcaption></figure>
<h2 id="making-it-stick">Making it stick</h2>
<ul class="check"><li>Agree an owner for each in-scope app before you start</li><li>Bring the app's discovered accounts into your regular <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="access-review" data-term="Access review" data-def="An Entra ID Governance feature that asks reviewers to confirm whether people still need access to a group, app or role, and can remove them automatically." data-post="/posts/guest-access-reviews/">access reviews</span></li><li>Turn on provisioning where the app supports it, so new accounts start in Entra</li><li>Remove local account creation rights from app admins where you can</li><li>Re-run discovery after each clean-up to show the trend</li></ul>
<details class="faq"><summary>Which apps does it work with?</summary><p>Apps connected to Entra for provisioning and governance. Check the documentation for the current list of supported connectors.</p></details>
<details class="faq"><summary>Will it change anything in the app?</summary><p>Discovery reports. Changes are yours to make, either in the app or through provisioning.</p></details>

<div class="sources"><b>Sources</b><ul><li><a href="https://learn.microsoft.com/en-us/entra/fundamentals/whats-new" target="_blank" rel="noopener">Microsoft Learn: What's new in Microsoft Entra</a></li></ul></div>
]]></content:encoded>
</item>
<item>
<title>Soft delete for Entra device objects: a safety net for device clean-ups</title>
<link>https://azureblog.co.uk/posts/entra-device-soft-delete-preview/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/entra-device-soft-delete-preview/</guid>
<pubDate>Wed, 10 Jun 2026 08:00:00 GMT</pubDate>
<category>Entra ID</category><category>Windows</category><category>What&#39;s new</category>
<description>Deleted device objects can now go into a recoverable state instead of disappearing instantly. A small preview feature that takes the fear out of stale device clean-ups.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/entra-device-soft-delete-preview.png" alt=""></p><p>Every tenant collects stale device objects: machines rebuilt, replaced or lost years ago. Cleaning them up is good hygiene, but deleting the wrong device is painful. Its identity, the BitLocker recovery keys stored against it and other security data go with it.</p>
<figure class="dg dg-timeline-wrap"><ol class="dg-tl"><li class="t-ok"><span class="dg-dot"></span><time>Active</time><span>Signing in, managed, compliant</span></li><li class="t-accent"><span class="dg-dot"></span><time>Stale</time><span>No sign-in for 90 days or more</span></li><li class="t-accent"><span class="dg-dot"></span><time>Disabled</time><span>Can't sign in. Wait and watch for complaints</span></li><li class=""><span class="dg-dot"></span><time>Soft-deleted</time><span>Recoverable, with keys and identity intact</span></li><li class="t-bad"><span class="dg-dot"></span><time>Hard-deleted</time><span>Gone after the retention period</span></li></ol><figcaption>A device's journey from active to gone.</figcaption></figure>

<p>A new preview adds <b>soft delete</b> for device objects. Deleted devices move into a recoverable state and can be restored within a retention period, with their identity and associated security artifacts intact. It covers <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="entra-join" data-term="Entra join" data-def="A corporate Windows device joined directly to Entra ID, with no on-premises domain membership needed.">Entra joined</span>, <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="entra-registered" data-term="Entra registered" data-def="A personal device where the user has added a work account. The device gets an identity in the tenant, but the user keeps control of it." data-post="/posts/byod-windows-entra-registration-ga/">Entra registered</span> and <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="hybrid-join" data-term="Hybrid join" data-def="A Windows device joined to on-premises Active Directory and also registered in Entra ID, so it can use device-based Conditional Access." data-post="/posts/hybrid-join-with-entra-kerberos-preview/">hybrid joined</span> devices.</p>

<h2 id="a-safer-stale-device-process">A safer stale-device process</h2>
<ol>
  <li>Find devices with no sign-in activity for a long period, such as 90 days or more, using the <code>approximateLastSignInDateTime</code> property.</li>
  <li><b>Disable</b> them first and wait. If nobody complains, move on.</li>
  <li>Check Intune and your other tools so you're not deleting a device that's still managed.</li>
  <li>Delete in batches. With soft delete, a mistake can be restored rather than rebuilt.</li>
</ol>
<div class="codebox"><div class="codebar"><span>powershell</span><button type="button" data-copy="" data-label="copy">copy</button></div><pre><code>Connect-MgGraph -Scopes "Device.Read.All"

$cutoff = (Get-Date).AddDays(-90)
Get-MgDevice -All -Property DisplayName,OperatingSystem,ApproximateLastSignInDateTime,AccountEnabled |
  Where-Object { $_.ApproximateLastSignInDateTime -lt $cutoff } |
  Sort-Object ApproximateLastSignInDateTime |
  Select-Object DisplayName, OperatingSystem, ApproximateLastSignInDateTime, AccountEnabled</code></pre></div>
<div class="callout"><strong>Preview caveat:</strong> while it's in preview, check the documentation for the current retention period before relying on it.</div>
<h2 id="before-you-delete-anything">Before you delete anything</h2>
<div class="tablewrap"><table>
  <tr><th>Check</th><th>Why</th></tr>
  <tr><td>Is it still in Intune?</td><td>A device that checks in with Intune but shows an old sign-in date may just be a device nobody signs in to interactively, such as a kiosk.</td></tr>
  <tr><td>Are BitLocker keys stored against it?</td><td>If the disk might still be needed, export the recovery key first.</td></tr>
  <tr><td>Is it a hybrid joined device?</td><td>Deleting it in Entra while it still exists in AD and syncs brings it back. Clean up in AD first.</td></tr>
  <tr><td>Is it an <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="autopilot" data-term="Windows Autopilot" data-def="A way to set up new Windows devices straight from the box: the device joins Entra ID, enrols in Intune and gets its apps and policies.">Autopilot</span> device?</td><td>Autopilot registrations are separate. Remove them only when the hardware is really leaving.</td></tr>
</table></div>
<h2 id="disable-in-bulk">Disable in bulk</h2>
<div class="codebox"><div class="codebar"><span>powershell</span><button type="button" data-copy="" data-label="copy">copy</button></div><pre><code>Connect-MgGraph -Scopes "Device.ReadWrite.All"
$cutoff = (Get-Date).AddDays(-90)
$stale = Get-MgDevice -All -Property Id,DisplayName,ApproximateLastSignInDateTime,AccountEnabled |
  Where-Object { $_.AccountEnabled -and $_.ApproximateLastSignInDateTime -lt $cutoff }
$stale | Export-Csv stale-devices.csv -NoTypeInformation   # keep a record
$stale | ForEach-Object { Update-MgDevice -DeviceId $_.Id -AccountEnabled:$false }</code></pre></div>
<details class="faq"><summary>Does disabling a device wipe it?</summary><p>No. It stops the device authenticating to Entra, which blocks device-based access. Wiping is an Intune action.</p></details>
<details class="faq"><summary>What's the benefit over just disabling?</summary><p>Disabled devices still clutter the directory and count against limits. Soft delete lets you finish the clean-up while keeping a way back.</p></details>

<div class="sources"><b>Sources</b><ul><li><a href="https://learn.microsoft.com/en-us/entra/fundamentals/whats-new" target="_blank" rel="noopener">Microsoft Learn: What's new in Microsoft Entra</a></li></ul></div>
]]></content:encoded>
</item>
<item>
<title>Cross-tenant group sync is GA: one group, many tenants</title>
<link>https://azureblog.co.uk/posts/cross-tenant-group-sync-ga/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/cross-tenant-group-sync-ga/</guid>
<pubDate>Wed, 03 Jun 2026 08:00:00 GMT</pubDate>
<category>Entra ID</category><category>Governance</category><category>What&#39;s new</category>
<description>Multi-tenant organisations can now sync security groups from a source tenant into target tenants, alongside the users already synced with cross-tenant sync.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/cross-tenant-group-sync-ga.png" alt=""></p><p>Cross-tenant synchronisation has been able to provision users from one Entra tenant into another for a while. That solved half the problem for multi-tenant organisations, such as groups with separate tenants per subsidiary or after an acquisition. The other half was groups: access in the target tenant still had to be granted by hand.</p>
<figure class="dg dg-hub-wrap"><div class="dg-scroll"><svg class="dg-hub" viewBox="0 0 640 340" width="640" height="340" role="img" aria-label="Source tenant connected to Subsidiary A, Subsidiary B, Acquired company, Shared services"><line class="spoke" x1="320" y1="170" x2="497" y2="82"/><line class="spoke" x1="320" y1="170" x2="497" y2="258"/><line class="spoke" x1="320" y1="170" x2="143" y2="258"/><line class="spoke" x1="320" y1="170" x2="143" y2="82"/><ellipse class="hubc" cx="320" cy="170" rx="99.84" ry="53.76"/><text class="hub-t" x="320" y="168" text-anchor="middle">Source tenant</text><text class="hub-n" x="320" y="188" text-anchor="middle">Group membership managed here</text><rect class="hn" x="439" y="64" width="115" height="36" rx="8"/><text class="hn-t" x="497" y="87" text-anchor="middle">Subsidiary A</text><rect class="hn" x="439" y="240" width="115" height="36" rx="8"/><text class="hn-t" x="497" y="263" text-anchor="middle">Subsidiary B</text><rect class="hn" x="70" y="240" width="146" height="36" rx="8"/><text class="hn-t" x="143" y="263" text-anchor="middle">Acquired company</text><rect class="hn" x="74" y="64" width="138" height="36" rx="8"/><text class="hn-t" x="143" y="87" text-anchor="middle">Shared services</text></svg></div><figcaption>One source group, consumed in several tenants.</figcaption></figure>

<p><b>Cross-tenant group synchronisation</b>, now generally available, syncs security groups from a source tenant to target tenants. Users and their group memberships arrive together.</p>

<h2 id="why-it-matters">Why it matters</h2>
<ul>
  <li><b>Single source of truth.</b> Manage membership once in the home tenant, and access follows into every tenant that consumes the group.</li>
  <li><b>Cleaner offboarding.</b> Remove someone from the source group and they lose access everywhere it's synced.</li>
  <li><b>Fewer duplicate groups</b> with slightly different names and memberships in each tenant.</li>
</ul>
<h2 id="how-it-works">How it works</h2>
<figure class="dg dg-seq-wrap"><div class="dg-scroll"><svg class="dg-seq" viewBox="0 0 670 462" width="670" height="462" role="img" aria-label="Sequence diagram: Source tenant, Sync engine, Target tenant"><line class="life" x1="125" y1="46" x2="125" y2="454"/><rect class="actor" x="30" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="125" y="28" text-anchor="middle">Source tenant</text><line class="life" x1="335" y1="46" x2="335" y2="454"/><rect class="actor" x="240" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="335" y="28" text-anchor="middle">Sync engine</text><line class="life" x1="545" y1="46" x2="545" y2="454"/><rect class="actor" x="450" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="545" y="28" text-anchor="middle">Target tenant</text><text class="lbl" x="230" y="84" text-anchor="middle">Sync config scopes users and</text><text class="lbl" x="230" y="99" text-anchor="middle">groups</text><text class="num" x="133" y="109" text-anchor="start">1</text><line class="arrow" x1="125" y1="114" x2="326" y2="114"/><polygon class="head" points="335,114 325,109 325,119"/><text class="lbl" x="440" y="149" text-anchor="middle">Provision users as members</text><text class="lbl" x="440" y="164" text-anchor="middle">(B2B)</text><text class="num" x="343" y="174" text-anchor="start">2</text><line class="arrow" x1="335" y1="179" x2="536" y2="179"/><polygon class="head" points="545,179 535,174 535,184"/><text class="lbl" x="440" y="214" text-anchor="middle">Provision security groups</text><text class="lbl" x="440" y="229" text-anchor="middle">and memberships</text><text class="num" x="343" y="239" text-anchor="start">3</text><line class="arrow t-accent" x1="335" y1="244" x2="536" y2="244"/><polygon class="head t-accent" points="545,244 535,239 535,249"/><rect class="note t-ok" x="416" y="267" width="250" height="44" rx="6"/><text class="lbl" x="541" y="285" text-anchor="middle">Target assigns the synced</text><text class="lbl" x="541" y="300" text-anchor="middle">group to apps</text><text class="lbl" x="230" y="358" text-anchor="middle">Member removed at source</text><text class="num" x="133" y="368" text-anchor="start">5</text><line class="arrow" x1="125" y1="373" x2="326" y2="373"/><polygon class="head" points="335,373 325,368 325,378"/><text class="lbl" x="440" y="408" text-anchor="middle">Membership removed in target</text><text class="num" x="343" y="418" text-anchor="start">6</text><line class="arrow t-ok" x1="335" y1="423" x2="536" y2="423"/><polygon class="head t-ok" points="545,423 535,418 535,428"/></svg></div><figcaption>Users and their groups arriving in a target tenant.</figcaption></figure>
<p>It builds on the cross-tenant synchronisation setup you may already have. The source tenant runs a sync configuration that scopes users and groups. The target tenant has to allow it in its cross-tenant access settings, with inbound user sync and automatic redemption enabled for the source tenant.</p>

<h2 id="before-you-start">Before you start</h2>
<ul>
  <li><b>Licensing.</b> Group sync needs Entra ID Governance licences.</li>
  <li><b>Scope carefully.</b> Only sync the groups the target tenant actually uses for access.</li>
  <li><b>Agree ownership.</b> Decide who in the target tenant may assign synced groups to apps and roles, and who can change them in the source.</li>
</ul>
<ul class="check"><li>Cross-tenant sync for users already working between the tenants</li><li>Inbound sync allowed in the target's cross-tenant access settings</li><li>Entra ID Governance licences in place</li><li>A short list of groups the target actually needs</li><li>Naming agreed, so synced groups are recognisable in the target</li><li>Owners in both tenants who know who changes what</li></ul>
<h2 id="what-it-doesn-t-do">What it doesn't do</h2>
<ul>
  <li>It syncs security groups for access. It isn't a way to replicate every Microsoft 365 group and its content between tenants.</li>
  <li>Groups are managed at the source. Changes made to the synced copy in the target are overwritten.</li>
  <li>Nested and dynamic group behaviour can differ from what you expect. Check the documentation for the current support before you design around it.</li>
</ul>
<details class="faq"><summary>Do target admins lose control?</summary><p>They keep control over what synced groups can access. They just don't manage membership, which is the point.</p></details>
<details class="faq"><summary>Can I use this for a merger?</summary><p>Yes, it's a strong fit while tenants coexist. It doesn't replace a tenant-to-tenant migration if the end goal is one tenant.</p></details>

<div class="sources"><b>Sources</b><ul><li><a href="https://learn.microsoft.com/en-us/entra/fundamentals/whats-new" target="_blank" rel="noopener">Microsoft Learn: What's new in Microsoft Entra</a></li></ul></div>
]]></content:encoded>
</item>
<item>
<title>System-preferred authentication now picks the first factor too</title>
<link>https://azureblog.co.uk/posts/system-preferred-first-factor-passwordless/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/system-preferred-first-factor-passwordless/</guid>
<pubDate>Wed, 27 May 2026 08:00:00 GMT</pubDate>
<category>Entra ID</category><category>Passkeys</category><category>What&#39;s new</category>
<description>Users with a passkey may now sign in without being asked for a password at all, and registration campaigns can prompt for passkeys. Two changes that quietly push tenants towards passwordless.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/system-preferred-first-factor-passwordless.png" alt=""></p><h2 id="system-preferred-first-factor-included">System-preferred, first factor included</h2>
<p>System-preferred authentication has been choosing the strongest second factor a user has registered for a while. It now applies to the <b>first factor</b> as well, in the Microsoft-managed state. Entra looks at the user's registered credentials and picks the highest-ranked method for each step.</p>
<figure class="dg dg-seq-wrap"><div class="dg-scroll"><svg class="dg-seq" viewBox="0 0 670 432" width="670" height="432" role="img" aria-label="Sequence diagram: User, Sign-in page, Entra ID"><line class="life" x1="125" y1="46" x2="125" y2="424"/><rect class="actor" x="30" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="125" y="28" text-anchor="middle">User</text><line class="life" x1="335" y1="46" x2="335" y2="424"/><rect class="actor" x="240" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="335" y="28" text-anchor="middle">Sign-in page</text><line class="life" x1="545" y1="46" x2="545" y2="424"/><rect class="actor" x="450" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="545" y="28" text-anchor="middle">Entra ID</text><text class="lbl" x="230" y="84" text-anchor="middle">Enters username</text><text class="num" x="133" y="94" text-anchor="start">1</text><line class="arrow" x1="125" y1="99" x2="326" y2="99"/><polygon class="head" points="335,99 325,94 325,104"/><text class="lbl" x="440" y="134" text-anchor="middle">Looks up registered methods</text><text class="num" x="343" y="144" text-anchor="start">2</text><line class="arrow" x1="335" y1="149" x2="536" y2="149"/><polygon class="head" points="545,149 535,144 535,154"/><rect class="note t-ok" x="416" y="172" width="250" height="44" rx="6"/><text class="lbl" x="541" y="190" text-anchor="middle">User has a passkey: rank it</text><text class="lbl" x="541" y="205" text-anchor="middle">first</text><text class="lbl" x="440" y="263" text-anchor="middle">Offer passkey sign-in</text><text class="num" x="537" y="273" text-anchor="end">4</text><line class="arrow" x1="545" y1="278" x2="344" y2="278"/><polygon class="head" points="335,278 345,273 345,283"/><text class="lbl" x="230" y="313" text-anchor="middle">Uses fingerprint, face or</text><text class="lbl" x="230" y="328" text-anchor="middle">PIN</text><text class="num" x="133" y="338" text-anchor="start">5</text><line class="arrow" x1="125" y1="343" x2="326" y2="343"/><polygon class="head" points="335,343 325,338 325,348"/><text class="lbl" x="440" y="378" text-anchor="middle">Passkey verified</text><text class="num" x="343" y="388" text-anchor="start">6</text><line class="arrow t-ok" x1="335" y1="393" x2="536" y2="393"/><polygon class="head t-ok" points="545,393 535,388 535,398"/></svg></div><figcaption>With system-preferred authentication, Entra offers the strongest method the user has.</figcaption></figure>

<p>In practice, a user with a <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="passkey" data-term="Passkey" data-def="A FIDO2 credential made of a key pair. The private key stays on the device or in a password manager and only signs in to the site it was created for." data-post="/posts/synced-passkeys-and-passkey-profiles/">passkey</span> can be offered the passkey straight away and sign in without typing a password. Nothing changes for users who only have a password and a weaker second factor.</p>

<h2 id="passkeys-in-registration-campaigns">Passkeys in registration campaigns</h2>
<p>The registration campaign (the "nudge" that prompts users at sign-in to set up a better method) can now target <b>passkeys</b>. That's the easiest way to drive adoption at scale: users are prompted at sign-in, can register in a minute, and IT doesn't have to chase anyone by email. The initial rollout is optimised for users in a passkey profile without restrictions.</p>

<h2 id="what-to-check">What to check</h2>
<ul>
  <li><b>Is your tenant Microsoft-managed?</b> The first-factor change only applies in the Microsoft-managed state. If you've explicitly disabled system-preferred authentication, you won't see it.</li>
  <li><b>Tell the service desk.</b> Users suddenly not being asked for a password will generate a few "is this safe?" calls.</li>
  <li><b>Plan the campaign.</b> Decide who's in scope, whether users can snooze and for how long, and communicate before switching it on.</li>
</ul>
<h2 id="what-users-actually-see">What users actually see</h2>
<ul>
  <li><b>Users with a passkey</b> are offered it straight away. Many will never type a password again.</li>
  <li><b>Users with Authenticator but no passkey</b> still enter a password, then get the strongest second factor they have.</li>
  <li><b>Users with only a password and SMS</b> see no change, except the registration campaign nudging them to add something stronger.</li>
</ul>
<p>A user can still choose another method from the "Other ways to sign in" link. System-preferred changes the default, not the options.</p>
<h2 id="measuring-progress">Measuring progress</h2>
<p>The <b>Authentication methods activity</b> report in the Entra admin center shows how many users are registered for each method and which methods they actually use to sign in. Watch the passwordless-capable percentage climb as the campaign runs.</p>

<details class="faq"><summary>Does this remove passwords from accounts?</summary><p>No. The password still exists; the user just isn't asked for it when a stronger first factor is available.</p></details>
<details class="faq"><summary>Can I turn system-preferred authentication off?</summary><p>Yes, but the first-factor change only applies in the Microsoft-managed state, and Microsoft recommends leaving it on.</p></details>

<div class="sources"><b>Sources</b><ul><li><a href="https://learn.microsoft.com/en-us/entra/fundamentals/whats-new" target="_blank" rel="noopener">Microsoft Learn: What's new in Microsoft Entra</a></li><li><a href="https://learn.microsoft.com/en-us/entra/identity/authentication/how-to-authentication-passkeys-fido2" target="_blank" rel="noopener">Microsoft Learn: Enable passkeys (FIDO2) in Entra ID</a></li></ul></div>
]]></content:encoded>
</item>
<item>
<title>Rolling out &quot;require compliant device&quot; without a flood of tickets</title>
<link>https://azureblog.co.uk/posts/rolling-out-require-compliant-device/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/rolling-out-require-compliant-device/</guid>
<pubDate>Wed, 20 May 2026 08:00:00 GMT</pubDate>
<category>Intune</category><category>Conditional Access</category><category>Entra ID</category>
<description>Requiring a compliant device is one of the strongest Conditional Access controls you can apply. It&#39;s also one of the easiest to get wrong. A step-by-step rollout.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/rolling-out-require-compliant-device.png" alt=""></p><p>A <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="conditional-access" data-term="Conditional Access" data-def="Entra ID&#39;s policy engine. Each policy says: if these users sign in to these apps under these conditions, then require (or block) something, such as MFA or a compliant device." data-post="/posts/conditional-access-report-only-and-what-if/">Conditional Access</span> policy that requires a compliant device means only devices managed by Intune and meeting your compliance rules can reach corporate data. Stolen passwords and tokens become far less useful. The challenge is rolling it out without blocking people who are doing nothing wrong.</p>
<figure class="dg dg-seq-wrap"><div class="dg-scroll"><svg class="dg-seq" viewBox="0 0 740 511" width="740" height="511" role="img" aria-label="Sequence diagram: Device, Intune, Entra ID, Microsoft 365"><line class="life" x1="107.5" y1="46" x2="107.5" y2="503"/><rect class="actor" x="30" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="107.5" y="28" text-anchor="middle">Device</text><line class="life" x1="282.5" y1="46" x2="282.5" y2="503"/><rect class="actor" x="205" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="282.5" y="28" text-anchor="middle">Intune</text><line class="life" x1="457.5" y1="46" x2="457.5" y2="503"/><rect class="actor" x="380" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="457.5" y="28" text-anchor="middle">Entra ID</text><line class="life" x1="632.5" y1="46" x2="632.5" y2="503"/><rect class="actor" x="555" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="632.5" y="28" text-anchor="middle">Microsoft 365</text><text class="lbl" x="195" y="84" text-anchor="middle">Checks in; reports</text><text class="lbl" x="195" y="99" text-anchor="middle">settings</text><text class="num" x="115.5" y="109" text-anchor="start">1</text><line class="arrow" x1="107.5" y1="114" x2="273.5" y2="114"/><polygon class="head" points="282.5,114 272.5,109 272.5,119"/><rect class="note" x="157.5" y="137" width="250" height="29" rx="6"/><text class="lbl" x="282.5" y="155" text-anchor="middle">Evaluates compliance policy</text><text class="lbl" x="370" y="213" text-anchor="middle">Writes isCompliant to</text><text class="lbl" x="370" y="228" text-anchor="middle">device object</text><text class="num" x="290.5" y="238" text-anchor="start">3</text><line class="arrow t-ok" x1="282.5" y1="243" x2="448.5" y2="243"/><polygon class="head t-ok" points="457.5,243 447.5,238 447.5,248"/><text class="lbl" x="370" y="278" text-anchor="middle">User opens Outlook</text><text class="num" x="115.5" y="288" text-anchor="start">4</text><line class="arrow" x1="107.5" y1="293" x2="623.5" y2="293"/><polygon class="head" points="632.5,293 622.5,288 622.5,298"/><text class="lbl" x="545" y="328" text-anchor="middle">Sign-in evaluated</text><text class="num" x="624.5" y="338" text-anchor="end">5</text><line class="arrow" x1="632.5" y1="343" x2="466.5" y2="343"/><polygon class="head" points="457.5,343 467.5,338 467.5,348"/><rect class="note t-ok" x="332.5" y="366" width="250" height="44" rx="6"/><text class="lbl" x="457.5" y="384" text-anchor="middle">Policy: require compliant</text><text class="lbl" x="457.5" y="399" text-anchor="middle">device; device is compliant</text><text class="lbl" x="282.5" y="457" text-anchor="middle">Access granted</text><text class="num" x="449.5" y="467" text-anchor="end">7</text><line class="arrow t-ok" x1="457.5" y1="472" x2="116.5" y2="472"/><polygon class="head t-ok" points="107.5,472 117.5,467 117.5,477"/></svg></div><figcaption>How compliance reaches a Conditional Access decision.</figcaption></figure>

<figure class="flow"><ol class="steps"><li><div class="node"><small>01</small>Fix compliance policies</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>02</small>Report-only policy</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>03</small>Plan exceptions</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>04</small>IT, then pilots</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>05</small>Everyone</div></li></ol></figure>

<h2 id="1-get-compliance-right-first">1. Get compliance right first</h2>
<ul>
  <li>Make sure every platform you allow has a <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="compliance-policy" data-term="Compliance policy" data-def="Intune rules a device must meet, such as encryption or a minimum OS version. Conditional Access can require a compliant device.">compliance policy</span> assigned.</li>
  <li>Review the tenant setting <b>Mark devices with no compliance policy assigned as</b>. Set it to <b>Not compliant</b>, so a missing policy isn't a free pass.</li>
  <li>Use a sensible <b>grace period</b> for non-urgent settings, so a device isn't blocked the moment something minor changes.</li>
  <li>Check the compliance report and fix the common failures, often OS version, BitLocker or Defender.</li>
</ul>

<h2 id="2-run-the-policy-in-report-only">2. Run the policy in report-only</h2>
<p>Create the Conditional Access policy targeting all users and all cloud apps, with the grant control <b>Require device to be marked as compliant</b>. Leave it in <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="report-only" data-term="Report-only mode" data-def="A Conditional Access policy state that evaluates the policy at every sign-in and logs the result, without enforcing it. Used to test impact before switching a policy on." data-post="/posts/conditional-access-report-only-and-what-if/">report-only</span> for a week or two and review who would be blocked.</p>

<h2 id="3-plan-the-exceptions">3. Plan the exceptions</h2>
<ul>
  <li><b>Break-glass accounts</b>: always excluded.</li>
  <li><b>Guests</b>: their devices are managed by their own organisation. Either exclude them or trust their compliance claims in cross-tenant access settings.</li>
  <li><b>Personal devices</b>: decide whether they're blocked or allowed limited browser access through a separate policy.</li>
  <li><b>Device enrolment</b>: new devices need to reach Intune before they're compliant. Check that enrolment and the Company Portal aren't blocked.</li>
</ul>
<figure class="dg dg-compare-wrap"><div class="dg-compare" style="--cols:3"><div class="dg-col t-ok"><h4>Managed devices</h4><ul><li>Full access in apps and browser</li><li>Must stay compliant</li></ul></div><div class="dg-col t-accent"><h4>Personal devices</h4><ul><li>Browser only, or app protection policies</li><li>Downloads blocked for sensitive data</li></ul></div><div class="dg-col t-bad"><h4>Excluded</h4><ul><li>Break-glass accounts</li><li>Specific service scenarios, documented and reviewed</li></ul></div></div><figcaption>Deciding what each group gets.</figcaption></figure>

<h2 id="4-enforce-in-waves">4. Enforce in waves</h2>
<p>Turn it on for IT first, then pilot departments, then everyone. Brief the service desk with the common fixes: run a compliance check in Company Portal, install pending updates, enable BitLocker.</p>
<div class="callout"><strong>Platform tip:</strong> on iOS and Android, users access apps through the browser or apps that support device state. Microsoft Edge is the safest choice on mobile for web apps behind this policy.</div>
<h2 id="what-users-will-see">What users will see</h2>
<p>When a device fails, the user gets a message that the device must be managed or compliant, with a link to Company Portal or settings to fix it. On Windows, the most common fixes are installing updates and restarting. The <a href="https://azureblog.co.uk/tools/troubleshoot/?t=compliance">compliance troubleshooting wizard</a> walks the service desk through the rest.</p>
<h2 id="watching-the-rollout">Watching the rollout</h2>
<ul>
  <li>Filter sign-in logs for Conditional Access failures with error 53000 (device not compliant) each morning of the rollout.</li>
  <li>Watch the Intune compliance report for devices stuck in "Not evaluated".</li>
  <li>Keep the previous wave stable for a few days before starting the next.</li>
</ul>

]]></content:encoded>
</item>
<item>
<title>Packaging Win32 apps for Intune: detection rules and return codes</title>
<link>https://azureblog.co.uk/posts/win32-app-packaging-detection-rules/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/win32-app-packaging-detection-rules/</guid>
<pubDate>Wed, 13 May 2026 08:00:00 GMT</pubDate>
<category>Intune</category><category>Windows</category>
<description>Most Win32 app failures in Intune come down to two things: a detection rule that doesn&#39;t match reality, and return codes Intune doesn&#39;t understand.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/win32-app-packaging-detection-rules.png" alt=""></p><h2 id="wrapping-the-app">Wrapping the app</h2>
<p>Intune deploys <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="win32" data-term="Win32 app" data-def="A traditional Windows app packaged as an .intunewin file so Intune can install it with detection rules, dependencies and return codes.">Win32 apps</span> as <code>.intunewin</code> files created with the Microsoft Win32 Content Prep Tool:</p>
<figure class="dg dg-seq-wrap"><div class="dg-scroll"><svg class="dg-seq" viewBox="0 0 670 461" width="670" height="461" role="img" aria-label="Sequence diagram: Intune, Management extension, Installer"><line class="life" x1="125" y1="46" x2="125" y2="453"/><rect class="actor" x="30" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="125" y="28" text-anchor="middle">Intune</text><line class="life" x1="335" y1="46" x2="335" y2="453"/><rect class="actor" x="240" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="335" y="28" text-anchor="middle">Management extension</text><line class="life" x1="545" y1="46" x2="545" y2="453"/><rect class="actor" x="450" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="545" y="28" text-anchor="middle">Installer</text><text class="lbl" x="230" y="84" text-anchor="middle">App assigned to device</text><text class="num" x="133" y="94" text-anchor="start">1</text><line class="arrow" x1="125" y1="99" x2="326" y2="99"/><polygon class="head" points="335,99 325,94 325,104"/><rect class="note" x="210" y="122" width="250" height="44" rx="6"/><text class="lbl" x="335" y="140" text-anchor="middle">Runs detection: already</text><text class="lbl" x="335" y="155" text-anchor="middle">installed?</text><text class="lbl" x="440" y="213" text-anchor="middle">Not detected: run install</text><text class="lbl" x="440" y="228" text-anchor="middle">command</text><text class="num" x="343" y="238" text-anchor="start">3</text><line class="arrow" x1="335" y1="243" x2="536" y2="243"/><polygon class="head" points="545,243 535,238 535,248"/><text class="lbl" x="440" y="278" text-anchor="middle">Return code (0, 3010…)</text><text class="num" x="537" y="288" text-anchor="end">4</text><line class="arrow t-accent" x1="545" y1="293" x2="344" y2="293"/><polygon class="head t-accent" points="335,293 345,288 345,298"/><rect class="note t-accent" x="210" y="316" width="250" height="29" rx="6"/><text class="lbl" x="335" y="334" text-anchor="middle">Runs detection again</text><text class="lbl" x="230" y="392" text-anchor="middle">Detected: Installed. Not</text><text class="lbl" x="230" y="407" text-anchor="middle">detected: Failed</text><text class="num" x="327" y="417" text-anchor="end">6</text><line class="arrow t-ok" x1="335" y1="422" x2="134" y2="422"/><polygon class="head t-ok" points="125,422 135,417 135,427"/></svg></div><figcaption>What Intune does when it installs a Win32 app.</figcaption></figure>

<div class="codebox"><div class="codebar"><span>shell</span><button type="button" data-copy="" data-label="copy">copy</button></div><pre><code>IntuneWinAppUtil.exe -c C:\Packages\7zip -s 7z-x64.msi -o C:\Packages\Output</code></pre></div>
<p>Everything in the source folder is included, so keep it tidy: the installer, any transforms and scripts, nothing else.</p>

<h2 id="install-and-uninstall-commands">Install and uninstall commands</h2>
<p>Both must run silently, with no user interaction:</p>
<div class="codebox"><div class="codebar"><span>shell</span><button type="button" data-copy="" data-label="copy">copy</button></div><pre><code>msiexec /i "7z-x64.msi" /qn /norestart
msiexec /x {23170F69-40C1-2702-0000-000001000000} /qn /norestart</code></pre></div>

<h2 id="detection-rules-decide-everything">Detection rules decide everything</h2>
<p>After installing, Intune runs your detection rule. If it doesn't find the app, the install is marked as failed, even if it worked perfectly. Choose the most reliable signal:</p>
<ul>
  <li><b>MSI product code</b>: best for MSIs. It can check the version too.</li>
  <li><b>File or folder</b>: check a file exists, optionally with a minimum version. Watch for 32-bit apps on 64-bit Windows, and tick the 32-bit option if needed.</li>
  <li><b>Registry</b>: an uninstall key or a value the installer writes.</li>
  <li><b>Script</b>: for complex cases. The app counts as detected if the script exits 0 <b>and</b> writes something to standard output.</li>
</ul>
<figure class="dg dg-decision-wrap"><div class="dg-decision"><div class="dg-q">What kind of installer is it?</div><div class="dg-branches"><div class="dg-branch t-ok"><span class="dg-if">MSI</span><span class="dg-then">MSI product code (optionally with version)</span></div><div class="dg-branch t-ok"><span class="dg-if">EXE that writes a normal uninstall key</span><span class="dg-then">Registry: the uninstall key and DisplayVersion</span></div><div class="dg-branch t-accent"><span class="dg-if">Portable app or custom setup</span><span class="dg-then">File or folder, with a version check</span></div><div class="dg-branch t-accent"><span class="dg-if">Anything complicated</span><span class="dg-then">Detection script that outputs text and exits 0</span></div></div></div><figcaption>Choosing a detection rule.</figcaption></figure>

<h2 id="return-codes">Return codes</h2>
<p>Intune needs to know what each installer exit code means. The defaults cover the common ones:</p>
<div class="tablewrap"><table>
  <tr><th>Code</th><th>Meaning</th></tr>
  <tr><td>0</td><td>Success</td></tr>
  <tr><td>1707</td><td>Success</td></tr>
  <tr><td>3010</td><td>Soft reboot: success, restart needed</td></tr>
  <tr><td>1641</td><td>Hard reboot: the installer started a restart</td></tr>
  <tr><td>1618</td><td>Retry: another installation is in progress</td></tr>
</table></div>
<p>Vendor installers often have their own codes. Check the vendor's documentation and add them.</p>

<h2 id="testing">Testing</h2>
<p>Test the install and uninstall commands as SYSTEM before uploading, for example using PsExec with <code>-s</code>. Then assign to a test device and check the logs in <code>C:\ProgramData\Microsoft\IntuneManagementExtension\Logs</code>.</p>
<h2 id="supersedence-and-dependencies">Supersedence and dependencies</h2>
<ul>
  <li><b>Supersedence</b> replaces an older app with a newer one. You can choose to uninstall the old version first or update in place.</li>
  <li><b>Dependencies</b> install another Win32 app first, such as a runtime the main app needs.</li>
</ul>
<h2 id="common-failures">Common failures</h2>
<div class="tablewrap"><table>
  <tr><th>Symptom</th><th>Usual cause</th></tr>
  <tr><td>Installs, but shows as failed</td><td>Detection rule doesn't match what the installer actually wrote</td></tr>
  <tr><td>Stuck on "Installing"</td><td>The installer is waiting for input. The command isn't really silent</td></tr>
  <tr><td>Error 0x87D1041C</td><td>The app was detected as not installed after the install ran</td></tr>
  <tr><td>Works on some devices only</td><td>32-bit versus 64-bit paths, or a missing dependency</td></tr>
</table></div>

]]></content:encoded>
</item>
<item>
<title>Entra Agent ID: giving AI agents real identities</title>
<link>https://azureblog.co.uk/posts/entra-agent-id-identities-for-ai-agents/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/entra-agent-id-identities-for-ai-agents/</guid>
<pubDate>Wed, 06 May 2026 08:00:00 GMT</pubDate>
<category>Entra ID</category><category>Security</category><category>Governance</category><category>What&#39;s new</category>
<description>AI agents now get first-class identities in Entra, with Conditional Access and lifecycle controls following. Here&#39;s why identity teams should get involved early.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/entra-agent-id-identities-for-ai-agents.png" alt=""></p><p>AI agents act on behalf of people and organisations. They read data, call APIs and increasingly take actions. Until recently they did it with whatever credentials were to hand: an <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="app-registration" data-term="App registration" data-def="The definition of an application in Entra ID: its ID, redirect URIs, credentials and the permissions it asks for.">app registration</span>, a service account, or a user's own token. That's hard to govern.</p>
<figure class="dg dg-hub-wrap"><div class="dg-scroll"><svg class="dg-hub" viewBox="0 0 640 340" width="640" height="340" role="img" aria-label="Agent identity connected to Human sponsor, Least-privilege permissions, Conditional Access, Sign-in and audit logs, Lifecycle workflows, Access reviews"><line class="spoke" x1="320" y1="170" x2="445" y2="62"/><line class="spoke" x1="320" y1="170" x2="570" y2="170"/><line class="spoke" x1="320" y1="170" x2="445" y2="278"/><line class="spoke" x1="320" y1="170" x2="195" y2="278"/><line class="spoke" x1="320" y1="170" x2="70" y2="170"/><line class="spoke" x1="320" y1="170" x2="195" y2="62"/><ellipse class="hubc" cx="320" cy="170" rx="99.84" ry="53.76"/><text class="hub-t" x="320" y="168" text-anchor="middle">Agent identity</text><text class="hub-n" x="320" y="188" text-anchor="middle">One per agent</text><rect class="hn" x="384" y="44" width="123" height="36" rx="8"/><text class="hn-t" x="445" y="67" text-anchor="middle">Human sponsor</text><rect class="hn" x="455" y="152" width="229" height="36" rx="8"/><text class="hn-t" x="570" y="175" text-anchor="middle">Least-privilege permissions</text><rect class="hn" x="365" y="260" width="161" height="36" rx="8"/><text class="hn-t" x="445" y="283" text-anchor="middle">Conditional Access</text><rect class="hn" x="99" y="260" width="191" height="36" rx="8"/><text class="hn-t" x="195" y="283" text-anchor="middle">Sign-in and audit logs</text><rect class="hn" x="-14" y="152" width="168" height="36" rx="8"/><text class="hn-t" x="70" y="175" text-anchor="middle">Lifecycle workflows</text><rect class="hn" x="130" y="44" width="130" height="36" rx="8"/><text class="hn-t" x="195" y="67" text-anchor="middle">Access reviews</text></svg></div><figcaption>Everything an agent identity should be connected to.</figcaption></figure>

<p><b>Microsoft Entra Agent ID</b>, now generally available, is an identity and authorisation framework for AI agents, built on OAuth 2.0 and supporting the Model Context Protocol (MCP) and agent-to-agent (A2A) scenarios.</p>

<h2 id="what-s-arriving-around-it">What's arriving around it</h2>
<ul>
  <li><b>Sponsorship lifecycle (GA).</b> Agent identities have a human sponsor. When that sponsor leaves, <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="lifecycle-workflows" data-term="Lifecycle Workflows" data-def="Entra ID Governance automation for joiner, mover and leaver tasks, such as issuing a Temporary Access Pass or removing group memberships.">Lifecycle Workflows</span> can transfer sponsorship to their manager, so no agent is left without an owner.</li>
  <li><b>Conditional Access for agents (preview).</b> Policies can target agent user accounts using custom security attributes, agent risk, device compliance and network conditions.</li>
  <li><b>Registry changes.</b> Microsoft has consolidated the Entra agent registry into Microsoft Agent 365. Check the current documentation if you registered agents through the earlier API.</li>
</ul>

<h2 id="what-identity-teams-should-do-now">What identity teams should do now</h2>
<ul>
  <li><b>Inventory existing agents</b> and what they authenticate with. Shared service accounts and long-lived secrets are the first things to replace.</li>
  <li><b>Insist on a sponsor</b> for every agent, just as every app registration should have owners.</li>
  <li><b>Apply least privilege.</b> An agent with broad Graph permissions is a large, automated attack surface.</li>
  <li><b>Log and review.</b> Treat agent sign-ins like any other <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="workload-identity" data-term="Workload identity" data-def="Any non-human identity, such as an app, service principal or managed identity, that signs in to access resources.">workload identity</span> and include them in your monitoring.</li>
</ul>
<h2 id="shared-credentials-versus-agent-identities">Shared credentials versus agent identities</h2>
<figure class="dg dg-compare-wrap"><div class="dg-compare" style="--cols:2"><div class="dg-col t-bad"><h4>Borrowed credentials</h4><p class="dg-sub">Service account, app secret or a user's token</p><ul><li>Hard to tell which agent did what</li><li>Permissions sized for the busiest use</li><li>Secrets that never expire</li><li>No owner when the builder leaves</li></ul></div><div class="dg-col t-ok"><h4>Agent ID</h4><p class="dg-sub">A dedicated identity per agent</p><ul><li>Every action attributable to one agent</li><li>Scoped permissions and policies</li><li>Sponsor with lifecycle handover</li><li>Visible in the same tools as other identities</li></ul></div></div><figcaption>Why the old approach doesn't scale.</figcaption></figure>
<h2 id="questions-to-ask-about-any-agent">Questions to ask about any agent</h2>
<ul class="check"><li>Is it acting as itself, or on behalf of a signed-in user?</li><li>Which data and APIs can it reach, and does it need all of them?</li><li>Who is the sponsor, and who takes over when they leave?</li><li>Can it be switched off quickly if it misbehaves?</li><li>Are its sign-ins and actions in your SIEM?</li></ul>
<details class="faq"><summary>Is an agent identity just a service principal?</summary><p>It's built on the same foundations, but with agent-specific features such as sponsorship and agent-aware policy. That's why Entra Backup and Recovery covers them alongside users and <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="service-principal" data-term="Service principal" data-def="An app&#39;s identity inside one tenant. An app registration is the global definition; the service principal is the local instance that gets permissions and signs in." data-post="/posts/managed-identities-vs-service-principals/">service principals</span>.</p></details>
<details class="faq"><summary>Do I need this if we don't build agents?</summary><p>Probably soon. Products you buy increasingly ship agents that act in your tenant. Knowing how they authenticate and what they can touch is part of approving them.</p></details>

<div class="sources"><b>Sources</b><ul><li><a href="https://learn.microsoft.com/en-us/entra/fundamentals/whats-new" target="_blank" rel="noopener">Microsoft Learn: What's new in Microsoft Entra</a></li></ul></div>
]]></content:encoded>
</item>
<item>
<title>Configurable token lifetimes are GA: when to shorten them (and when not to)</title>
<link>https://azureblog.co.uk/posts/configurable-token-lifetimes-ga/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/configurable-token-lifetimes-ga/</guid>
<pubDate>Wed, 29 Apr 2026 08:00:00 GMT</pubDate>
<category>Entra ID</category><category>Security</category><category>What&#39;s new</category>
<description>You can now set the lifetime of access, ID and SAML tokens per application. Useful for sensitive apps, but there are better tools for most session problems.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/configurable-token-lifetimes-ga.png" alt=""></p><p>Token lifetime policies let you change how long access tokens, ID tokens and <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="saml" data-term="SAML" data-def="Security Assertion Markup Language: an XML-based single sign-on standard where Entra ID sends a signed assertion about the user to the app." data-post="/posts/saml-certificate-rollover/">SAML</span> tokens from the Microsoft identity platform stay valid. You create a policy and assign it to specific applications or <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="service-principal" data-term="Service principal" data-def="An app&#39;s identity inside one tenant. An app registration is the global definition; the service principal is the local instance that gets permissions and signs in." data-post="/posts/managed-identities-vs-service-principals/">service principals</span>.</p>
<figure class="dg dg-compare-wrap"><div class="dg-compare" style="--cols:3"><div class="dg-col t-accent"><h4>Token lifetime policy</h4><ul><li>Access token lifetime</li><li>ID token lifetime</li><li>SAML token lifetime</li><li>Per app or service principal</li></ul></div><div class="dg-col t-ok"><h4>Conditional Access session controls</h4><ul><li>How often users sign in again</li><li>Persistent browser sessions</li><li>Applied by policy to users and apps</li></ul></div><div class="dg-col t-ok"><h4>Continuous access evaluation</h4><ul><li>Near-real-time revocation</li><li>Reacts to account and location changes</li><li>In supporting services</li></ul></div></div><figcaption>Which tool controls which part of a session.</figcaption></figure>

<h2 id="when-it-helps">When it helps</h2>
<ul>
  <li><b>Sensitive applications.</b> A shorter access token lifetime means a stolen token is useful for less time.</li>
  <li><b>SAML apps with long sessions.</b> Controlling the SAML token lifetime can tighten how long an assertion is accepted.</li>
  <li><b>Long-running automation.</b> In some cases a longer access token lifetime reduces token refresh churn.</li>
</ul>

<h2 id="when-it-doesn-t">When it doesn't</h2>
<p>Token lifetime policies don't control refresh tokens or session tokens. If the problem is "how often should users sign in again?", the answer is <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="conditional-access" data-term="Conditional Access" data-def="Entra ID&#39;s policy engine. Each policy says: if these users sign in to these apps under these conditions, then require (or block) something, such as MFA or a compliant device." data-post="/posts/conditional-access-report-only-and-what-if/">Conditional Access</span> session controls:</p>
<ul>
  <li><b>Sign-in frequency</b> sets how often users must reauthenticate.</li>
  <li><b>Persistent browser session</b> controls whether the browser stays signed in after it closes.</li>
  <li><b>Continuous access evaluation (CAE)</b> lets supporting services revoke access quickly when something changes, such as a disabled account or a new location, without needing short tokens everywhere.</li>
</ul>

<h2 id="practical-guidance">Practical guidance</h2>
<ul>
  <li>Change lifetimes only for specific apps with a clear reason. Don't apply a short lifetime tenant-wide.</li>
  <li>Test with the app owner. Some apps behave badly when tokens expire mid-session.</li>
  <li>Document the policy and why it exists, so the next engineer doesn't remove it or copy it blindly.</li>
</ul>
<h2 id="choosing-a-lifetime">Choosing a lifetime</h2>
<figure class="dg dg-decision-wrap"><div class="dg-decision"><div class="dg-q">What problem are you solving?</div><div class="dg-branches"><div class="dg-branch t-accent"><span class="dg-if">Users stay signed in too long</span><span class="dg-then">Use Conditional Access sign-in frequency instead</span></div><div class="dg-branch t-ok"><span class="dg-if">Stolen tokens should be useful for less time</span><span class="dg-then">Shorter access token lifetime for that app</span></div><div class="dg-branch t-accent"><span class="dg-if">Disabled users keep access too long</span><span class="dg-then">Continuous access evaluation is the better fix</span></div></div></div><figcaption>Do you need a token lifetime policy at all?</figcaption></figure>
<h2 id="testing-a-change">Testing a change</h2>
<ol>
  <li>Create the policy and assign it to a test copy of the app, or the real app with a pilot group.</li>
  <li>Sign in, then decode the access token with the <a href="https://azureblog.co.uk/tools/jwt/">JWT decoder</a> and compare <code>iat</code> and <code>exp</code> to confirm the new lifetime.</li>
  <li>Use the app past the lifetime to make sure it renews tokens quietly instead of failing.</li>
  <li>Roll out to everyone and record why the policy exists.</li>
</ol>

<div class="sources"><b>Sources</b><ul><li><a href="https://learn.microsoft.com/en-us/entra/identity-platform/configurable-token-lifetimes" target="_blank" rel="noopener">Microsoft Learn: Configurable token lifetimes</a></li><li><a href="https://learn.microsoft.com/en-us/entra/fundamentals/whats-new" target="_blank" rel="noopener">Microsoft Learn: What's new in Microsoft Entra</a></li></ul></div>
]]></content:encoded>
</item>
<item>
<title>Require phishing-resistant MFA on every PIM activation</title>
<link>https://azureblog.co.uk/posts/pim-activation-conditional-access-context/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/pim-activation-conditional-access-context/</guid>
<pubDate>Wed, 22 Apr 2026 08:00:00 GMT</pubDate>
<category>Entra ID</category><category>Governance</category><category>Conditional Access</category><category>What&#39;s new</category>
<description>PIM can now require a Conditional Access authentication context every time someone activates a role, and it&#39;s generally available. Here&#39;s how to set it up properly.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/pim-activation-conditional-access-context.png" alt=""></p><p>Privileged Identity Management has always been able to require <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="mfa" data-term="MFA" data-def="Multifactor authentication: proving who you are with more than one factor, such as something you know, something you have, or something you are.">MFA</span> on activation. The problem is that a user who already signed in with MFA often isn't challenged again, and any MFA method counts, including weaker ones. Requiring a <b>Conditional Access authentication context</b> on activation fixes both: you decide exactly what the user must prove, and it's checked every time they activate.</p>
<figure class="dg dg-seq-wrap"><div class="dg-scroll"><svg class="dg-seq" viewBox="0 0 740 448" width="740" height="448" role="img" aria-label="Sequence diagram: Admin, PIM, Conditional Access, Entra ID"><line class="life" x1="107.5" y1="46" x2="107.5" y2="440"/><rect class="actor" x="30" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="107.5" y="28" text-anchor="middle">Admin</text><line class="life" x1="282.5" y1="46" x2="282.5" y2="440"/><rect class="actor" x="205" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="282.5" y="28" text-anchor="middle">PIM</text><line class="life" x1="457.5" y1="46" x2="457.5" y2="440"/><rect class="actor" x="380" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="457.5" y="28" text-anchor="middle">Conditional Access</text><line class="life" x1="632.5" y1="46" x2="632.5" y2="440"/><rect class="actor" x="555" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="632.5" y="28" text-anchor="middle">Entra ID</text><text class="lbl" x="195" y="84" text-anchor="middle">Request activation of</text><text class="lbl" x="195" y="99" text-anchor="middle">Global Administrator</text><text class="num" x="115.5" y="109" text-anchor="start">1</text><line class="arrow" x1="107.5" y1="114" x2="273.5" y2="114"/><polygon class="head" points="282.5,114 272.5,109 272.5,119"/><text class="lbl" x="370" y="149" text-anchor="middle">Requires context:</text><text class="lbl" x="370" y="164" text-anchor="middle">Privileged role</text><text class="lbl" x="370" y="179" text-anchor="middle">activation</text><text class="num" x="290.5" y="189" text-anchor="start">2</text><line class="arrow" x1="282.5" y1="194" x2="448.5" y2="194"/><polygon class="head" points="457.5,194 447.5,189 447.5,199"/><text class="lbl" x="282.5" y="229" text-anchor="middle">Prove it: phishing-resistant MFA from a</text><text class="lbl" x="282.5" y="244" text-anchor="middle">compliant device</text><text class="num" x="449.5" y="254" text-anchor="end">3</text><line class="arrow t-accent" x1="457.5" y1="259" x2="116.5" y2="259"/><polygon class="head t-accent" points="107.5,259 117.5,254 117.5,264"/><text class="lbl" x="282.5" y="294" text-anchor="middle">Passkey sign-in on a compliant laptop</text><text class="num" x="115.5" y="304" text-anchor="start">4</text><line class="arrow t-ok" x1="107.5" y1="309" x2="448.5" y2="309"/><polygon class="head t-ok" points="457.5,309 447.5,304 447.5,314"/><text class="lbl" x="370" y="344" text-anchor="middle">Context satisfied</text><text class="num" x="449.5" y="354" text-anchor="end">5</text><line class="arrow" x1="457.5" y1="359" x2="291.5" y2="359"/><polygon class="head" points="282.5,359 292.5,354 292.5,364"/><text class="lbl" x="457.5" y="394" text-anchor="middle">Role activated for the set duration</text><text class="num" x="290.5" y="404" text-anchor="start">6</text><line class="arrow t-ok" x1="282.5" y1="409" x2="623.5" y2="409"/><polygon class="head t-ok" points="632.5,409 622.5,404 622.5,414"/></svg></div><figcaption>Activation with an authentication context.</figcaption></figure>

<figure class="flow"><ol class="steps"><li><div class="node"><small>01</small>Admin requests role activation</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>02</small>PIM requests authentication context</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>03</small>Conditional Access demands phishing-resistant MFA</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>04</small>Role active, time-boxed</div></li></ol></figure>

<h2 id="how-it-fits-together">How it fits together</h2>
<ul>
  <li><b>Authentication context</b> is a label, such as "Privileged activation", that apps and services can request.</li>
  <li><b>A Conditional Access policy</b> targets that label and sets the requirements, for example <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="phishing-resistant" data-term="Phishing-resistant MFA" data-def="Sign-in methods bound to the real site, so they can&#39;t be relayed by a fake one: passkeys (FIDO2), Windows Hello for Business and multifactor certificate-based authentication." data-post="/posts/authentication-strengths-explained/">phishing-resistant</span> MFA from a compliant device.</li>
  <li><b>PIM</b> requests the label when someone activates a role, so the policy is enforced at that moment.</li>
</ul>

<h2 id="setting-it-up">Setting it up</h2>
<ol>
  <li><b>Create the context.</b> In the Entra admin center, go to <b>Conditional Access → Authentication contexts</b> and create one, for example <i>Privileged role activation</i>. Make sure it's published to apps.</li>
  <li><b>Create the policy.</b> Make a new <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="conditional-access" data-term="Conditional Access" data-def="Entra ID&#39;s policy engine. Each policy says: if these users sign in to these apps under these conditions, then require (or block) something, such as MFA or a compliant device." data-post="/posts/conditional-access-report-only-and-what-if/">Conditional Access</span> policy targeting your admins. Under <b>Target resources</b>, choose <b>Authentication context</b> and select it. Under <b>Grant</b>, require the <b>Phishing-resistant MFA</b> <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="authentication-strength" data-term="Authentication strength" data-def="A Conditional Access grant control that requires specific sign-in methods, such as phishing-resistant MFA, rather than any MFA method." data-post="/posts/authentication-strengths-explained/">authentication strength</span>. Optionally add a compliant device requirement and a sign-in frequency of every time.</li>
  <li><b>Test in report-only mode</b>, then switch it on.</li>
  <li><b>Configure PIM.</b> Go to <b>Identity governance → Privileged Identity Management → Microsoft Entra roles → Settings</b>, pick a role and choose <b>Edit</b>. Under activation, select <b>On activation, require Microsoft Entra Conditional Access authentication context</b> and choose your context.</li>
  <li><b>Repeat for each high-value role</b>, starting with Global Administrator, Privileged Role Administrator, Security Administrator and Conditional Access Administrator.</li>
</ol>

<h2 id="things-to-watch">Things to watch</h2>
<ul>
  <li><b>Make sure admins have a passkey first.</b> If you require phishing-resistant MFA, admins without a <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="passkey" data-term="Passkey" data-def="A FIDO2 credential made of a key pair. The private key stays on the device or in a password manager and only signs in to the site it was created for." data-post="/posts/synced-passkeys-and-passkey-profiles/">passkey</span> or security key can't activate. Use a registration campaign or passkey profile ahead of time.</li>
  <li><b>Exclude your break-glass accounts</b> from the policy, and monitor their sign-ins separately.</li>
  <li><b>Apply it to PIM for Groups too</b> if you use groups to grant privileged access. The same setting exists in group role settings.</li>
</ul>
<div class="callout"><strong>Why bother:</strong> stolen sessions and phishing kits that relay MFA prompts are common. An attacker holding a stolen token still can't elevate if each activation demands a fresh, phishing-resistant sign-in.</div>
<figure class="dg dg-compare-wrap"><div class="dg-compare" style="--cols:2"><div class="dg-col"><h4>Require MFA on activation</h4><p class="dg-sub">The old setting</p><ul><li>Any MFA method counts</li><li>Often satisfied by the existing session</li><li>No device or location conditions</li></ul></div><div class="dg-col t-ok"><h4>Require authentication context</h4><p class="dg-sub">Recommended</p><ul><li>You choose the authentication strength</li><li>Can require a compliant device</li><li>Sign-in frequency every time forces a fresh check</li><li>Policy is visible and testable in Conditional Access</li></ul></div></div><figcaption>Plain MFA on activation versus an authentication context.</figcaption></figure>
<ul class="check"><li>Exclude <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="break-glass" data-term="Break-glass account" data-def="An emergency cloud-only Global Administrator account, excluded from normal policies and closely monitored, used only when normal admin access fails." data-post="/posts/break-glass-accounts-done-right/">break-glass accounts</span> from the policy</li><li>Make sure every admin has a phishing-resistant method registered first</li><li>Test in <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="report-only" data-term="Report-only mode" data-def="A Conditional Access policy state that evaluates the policy at every sign-in and logs the result, without enforcing it. Used to test impact before switching a policy on." data-post="/posts/conditional-access-report-only-and-what-if/">report-only</span> mode, then use <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="what-if" data-term="What If" data-def="A Conditional Access tool that shows which policies would apply to a given user, app and set of conditions, without anyone having to sign in." data-post="/posts/conditional-access-report-only-and-what-if/">What If</span></li><li>Apply the context to every privileged role, not just Global Administrator</li><li>Check scripted activations still work, since the token must satisfy the context</li></ul>
<details class="faq"><summary>Do I still need the MFA on activation setting?</summary><p>No. The <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="authentication-context" data-term="Authentication context" data-def="A label (such as c1) that an app or service like PIM can request at a sensitive moment. A Conditional Access policy targeting the label sets what the user must prove.">authentication context</span> replaces it and does more. Use one or the other per role.</p></details>
<details class="faq"><summary>Does it work for Azure resource roles and groups?</summary><p>Yes. <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="pim" data-term="PIM" data-def="Privileged Identity Management: users are made eligible for admin roles and activate them only when needed, for a limited time, with justification and checks." data-post="/posts/activate-pim-roles-with-powershell/">PIM</span> for Azure roles and PIM for Groups can require an authentication context too.</p></details>

<div class="sources"><b>Sources</b><ul>
  <li><a href="https://learn.microsoft.com/en-us/entra/fundamentals/whats-new" target="_blank" rel="noopener">Microsoft Learn: What's new in Microsoft Entra</a></li>
  <li><a href="https://learn.microsoft.com/en-us/entra/id-governance/privileged-identity-management/groups-role-settings#on-activation-require-microsoft-entra-conditional-access-authentication-context" target="_blank" rel="noopener">Microsoft Learn: Require Conditional Access authentication context on activation</a></li>
</ul></div>
]]></content:encoded>
</item>
<item>
<title>Intune Remediations: find and fix problems before users notice</title>
<link>https://azureblog.co.uk/posts/intune-remediations-scripts/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/intune-remediations-scripts/</guid>
<pubDate>Wed, 15 Apr 2026 08:00:00 GMT</pubDate>
<category>Intune</category><category>Windows</category><category>PowerShell</category>
<description>Remediations pair a detection script with a fix script and run them on a schedule. It&#39;s one of the most useful and underused features in Intune.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/intune-remediations-scripts.png" alt=""></p><p><span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="remediations" data-term="Remediations" data-def="Intune script packages: a detection script checks for a problem, and a remediation script fixes it when found.">Remediations</span> (once called Proactive Remediations) run a pair of PowerShell scripts on Windows devices on a schedule. The <b>detection</b> script checks for a problem. If it finds one, the <b>remediation</b> script fixes it. Intune reports on both, so you can see how many devices had the problem and how many were fixed.</p>
<figure class="flow"><ol class="steps"><li><div class="node"><small>01</small>Detection script runs</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>02</small>Exit 1: problem found</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>03</small>Remediation script fixes it</div></li><li><span class="wire" aria-hidden="true"></span><div class="node"><small>04</small>Results reported in Intune</div></li></ol></figure>

<h2 id="how-the-scripts-talk-to-intune">How the scripts talk to Intune</h2>
<ul>
  <li><b>Exit 0</b> from detection means "all good". Nothing else runs.</li>
  <li><b>Exit 1</b> from detection means "problem found". The remediation script runs.</li>
  <li>Whatever the script writes to output appears in the Intune report, so write something useful.</li>
</ul>
<figure class="dg dg-decision-wrap"><div class="dg-decision"><div class="dg-q">Detection script exit code?</div><div class="dg-branches"><div class="dg-branch t-ok"><span class="dg-if">Exit 0</span><span class="dg-then">No issue detected. Remediation doesn't run.</span></div><div class="dg-branch t-accent"><span class="dg-if">Exit 1</span><span class="dg-then">Issue detected. Remediation script runs, then detection runs again to confirm.</span></div><div class="dg-branch t-bad"><span class="dg-if">Error or timeout</span><span class="dg-then">Reported as failed in Intune. Check the output.</span></div></div></div><figcaption>What happens on each run.</figcaption></figure>

<h2 id="example-clear-a-full-temp-folder">Example: clear a full temp folder</h2>
<p>Detection:</p>
<div class="codebox"><div class="codebar"><span>powershell</span><button type="button" data-copy="" data-label="copy">copy</button></div><pre><code>$sizeGB = (Get-ChildItem "$env:SystemRoot\Temp" -Recurse -Force -ErrorAction SilentlyContinue |
  Measure-Object Length -Sum).Sum / 1GB
if ($sizeGB -gt 5) { Write-Output "Temp is $([math]::Round($sizeGB,1)) GB"; exit 1 }
Write-Output "Temp OK"; exit 0</code></pre></div>
<p>Remediation:</p>
<div class="codebox"><div class="codebar"><span>powershell</span><button type="button" data-copy="" data-label="copy">copy</button></div><pre><code>Get-ChildItem "$env:SystemRoot\Temp" -Recurse -Force -ErrorAction SilentlyContinue |
  Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-7) } |
  Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
Write-Output "Cleaned temp folder"; exit 0</code></pre></div>

<h2 id="good-uses">Good uses</h2>
<ul>
  <li>Restarting a service that keeps stopping</li>
  <li>Fixing a registry value that users or apps keep changing</li>
  <li>Detection-only reports, such as finding devices with a specific app version or a certificate about to expire</li>
</ul>

<h2 id="things-to-know">Things to know</h2>
<ul>
  <li><b>Licensing:</b> Remediations needs Windows Enterprise E3/E5 or equivalent (included in Microsoft 365 E3/E5).</li>
  <li><b>Context:</b> scripts run as SYSTEM by default. Choose "run as logged-on user" for user-level fixes.</li>
  <li><b>Use Run remediation</b> on a single device for on-demand fixes from the device page.</li>
</ul>
<h2 id="writing-scripts-that-behave">Writing scripts that behave</h2>
<ul class="check"><li>Make detection read-only: it should only check, never change anything</li><li>Make remediation safe to run twice (idempotent)</li><li>Write one clear line of output: it appears in the Intune report columns</li><li>Keep scripts short and fast: they run on every device in scope</li><li>Sign scripts if your devices enforce signed PowerShell, or allow unsigned in the remediation settings</li></ul>
<h2 id="reading-the-results">Reading the results</h2>
<p>In Intune, open <b>Devices → Scripts and remediations → Remediations</b> and select the package. The overview shows how many devices had the issue, how many were fixed and how many failed. The device status view adds the detection and remediation output for each device, so useful output lines pay off here.</p>
<h2 id="troubleshooting">Troubleshooting</h2>
<ul>
  <li>Logs are on the device under <code>C:\ProgramData\Microsoft\IntuneManagementExtension\Logs</code>, mainly <code>AgentExecutor.log</code> and <code>IntuneManagementExtension.log</code>.</li>
  <li>A script that works in your console but fails in Intune is usually running as SYSTEM, where user paths and mapped drives don't exist.</li>
  <li>64-bit versus 32-bit PowerShell changes which registry view you see. Choose 64-bit in the package settings if you read <code>HKLM\SOFTWARE</code>.</li>
</ul>

]]></content:encoded>
</item>
<item>
<title>Tenant configuration management: snapshot your Entra config and catch drift</title>
<link>https://azureblog.co.uk/posts/entra-tenant-configuration-management/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/entra-tenant-configuration-management/</guid>
<pubDate>Wed, 08 Apr 2026 08:00:00 GMT</pubDate>
<category>Entra ID</category><category>Governance</category><category>What&#39;s new</category>
<description>New APIs let you snapshot tenant configuration as JSON and monitor it for drift. Configuration as code for Entra just got much more practical.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/entra-tenant-configuration-management.png" alt=""></p><p>Most Entra tenants are configured by hand, one portal click at a time. That works until someone changes a setting nobody wrote down, and the question becomes "what was it before?"</p>
<figure class="dg dg-seq-wrap"><div class="dg-scroll"><svg class="dg-seq" viewBox="0 0 740 447" width="740" height="447" role="img" aria-label="Sequence diagram: Admin, Config management API, Tenant, Git / alerts"><line class="life" x1="107.5" y1="46" x2="107.5" y2="439"/><rect class="actor" x="30" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="107.5" y="28" text-anchor="middle">Admin</text><line class="life" x1="282.5" y1="46" x2="282.5" y2="439"/><rect class="actor" x="205" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="282.5" y="28" text-anchor="middle">Config management API</text><line class="life" x1="457.5" y1="46" x2="457.5" y2="439"/><rect class="actor" x="380" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="457.5" y="28" text-anchor="middle">Tenant</text><line class="life" x1="632.5" y1="46" x2="632.5" y2="439"/><rect class="actor" x="555" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="632.5" y="28" text-anchor="middle">Git / alerts</text><text class="lbl" x="195" y="84" text-anchor="middle">Take a snapshot after</text><text class="lbl" x="195" y="99" text-anchor="middle">an approved change</text><text class="num" x="115.5" y="109" text-anchor="start">1</text><line class="arrow" x1="107.5" y1="114" x2="273.5" y2="114"/><polygon class="head" points="282.5,114 272.5,109 272.5,119"/><text class="lbl" x="370" y="149" text-anchor="middle">Read configuration as</text><text class="lbl" x="370" y="164" text-anchor="middle">JSON</text><text class="num" x="290.5" y="174" text-anchor="start">2</text><line class="arrow" x1="282.5" y1="179" x2="448.5" y2="179"/><polygon class="head" points="457.5,179 447.5,174 447.5,184"/><text class="lbl" x="457.5" y="214" text-anchor="middle">Store as the baseline</text><text class="num" x="290.5" y="224" text-anchor="start">3</text><line class="arrow t-ok" x1="282.5" y1="229" x2="623.5" y2="229"/><polygon class="head t-ok" points="632.5,229 622.5,224 622.5,234"/><text class="lbl" x="370" y="264" text-anchor="middle">Monitor runs on a</text><text class="lbl" x="370" y="279" text-anchor="middle">schedule</text><text class="num" x="290.5" y="289" text-anchor="start">4</text><line class="arrow" x1="282.5" y1="294" x2="448.5" y2="294"/><polygon class="head" points="457.5,294 447.5,289 447.5,299"/><rect class="note t-accent" x="157.5" y="317" width="250" height="29" rx="6"/><text class="lbl" x="282.5" y="335" text-anchor="middle">Compare with the baseline</text><text class="lbl" x="457.5" y="393" text-anchor="middle">Drift found: raise an alert</text><text class="num" x="290.5" y="403" text-anchor="start">6</text><line class="arrow t-bad" x1="282.5" y1="408" x2="623.5" y2="408"/><polygon class="head t-bad" points="632.5,408 622.5,403 622.5,413"/></svg></div><figcaption>Baseline, monitor, alert.</figcaption></figure>

<p><b>Tenant configuration management</b> APIs, now generally available, let you take a <b>snapshot</b> of tenant configuration as JSON and set up <b>monitors</b> that detect drift from a known-good baseline. An admin center experience for managing monitors is in preview.</p>

<h2 id="ways-to-use-it">Ways to use it</h2>
<ul>
  <li><b>Baseline after a change.</b> When a CAB-approved change goes in, snapshot the result. That becomes the reference.</li>
  <li><b>Drift alerts.</b> Monitor the settings that matter most, such as authentication methods, <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="conditional-access" data-term="Conditional Access" data-def="Entra ID&#39;s policy engine. Each policy says: if these users sign in to these apps under these conditions, then require (or block) something, such as MFA or a compliant device." data-post="/posts/conditional-access-report-only-and-what-if/">Conditional Access</span> and external collaboration, and alert on unexpected changes.</li>
  <li><b>Multi-tenant consistency.</b> Compare test and production tenants, or several production tenants, against the same baseline.</li>
  <li><b>Store snapshots in Git.</b> A JSON snapshot in source control gives you a readable history of your tenant over time.</li>
</ul>

<h2 id="how-it-fits-with-backup">How it fits with backup</h2>
<p>Entra Backup and Recovery restores directory objects from the last few days. Configuration snapshots answer a different question: <i>is my tenant still configured the way I intended?</i> Use both.</p>
<figure class="dg dg-compare-wrap"><div class="dg-compare" style="--cols:2"><div class="dg-col t-ok"><h4>Backup and Recovery</h4><p class="dg-sub">Can I undo a recent change?</p><ul><li>Automatic daily backups</li><li>Seven days of history</li><li>Restores objects and properties</li></ul></div><div class="dg-col t-accent"><h4>Configuration management</h4><p class="dg-sub">Is my tenant still how I intended?</p><ul><li>Snapshots you choose to take</li><li>Monitors that detect drift</li><li>JSON you can keep in Git for years</li></ul></div></div><figcaption>Two tools, two questions.</figcaption></figure>
<h2 id="what-to-monitor-first">What to monitor first</h2>
<ul class="check"><li>Authentication methods policy</li><li>Conditional Access policies and <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="named-location" data-term="Named location" data-def="A set of IP ranges or countries saved in Entra ID so Conditional Access policies can include or exclude them." data-post="/posts/named-locations-in-conditional-access/">named locations</span></li><li>External collaboration and cross-tenant access settings</li><li>User consent and <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="app-registration" data-term="App registration" data-def="The definition of an application in Entra ID: its ID, redirect URIs, credentials and the permissions it asks for.">app registration</span> settings</li><li>Privileged role settings</li></ul>
<details class="faq"><summary>Will a monitor stop someone making a change?</summary><p>No. It detects and reports. Prevention is still Conditional Access, <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="pim" data-term="PIM" data-def="Privileged Identity Management: users are made eligible for admin roles and activate them only when needed, for a limited time, with justification and checks." data-post="/posts/activate-pim-roles-with-powershell/">PIM</span> and change control.</p></details>
<details class="faq"><summary>How do I handle approved changes?</summary><p>Update the baseline as the last step of the change. If a monitor fires and there's no matching change record, that's worth a look.</p></details>

<div class="sources"><b>Sources</b><ul><li><a href="https://learn.microsoft.com/en-us/entra/fundamentals/whats-new" target="_blank" rel="noopener">Microsoft Learn: What's new in Microsoft Entra</a></li></ul></div>
]]></content:encoded>
</item>
<item>
<title>Hybrid join without Entra Connect: hybrid join using Entra Kerberos</title>
<link>https://azureblog.co.uk/posts/hybrid-join-with-entra-kerberos-preview/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/hybrid-join-with-entra-kerberos-preview/</guid>
<pubDate>Wed, 01 Apr 2026 08:00:00 GMT</pubDate>
<category>Entra ID</category><category>Hybrid identity</category><category>Windows</category><category>What&#39;s new</category>
<description>A new preview lets Windows devices become Entra hybrid joined during provisioning, without device sync through Entra Connect or AD FS.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/hybrid-join-with-entra-kerberos-preview.png" alt=""></p><p><span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="hybrid-join" data-term="Hybrid join" data-def="A Windows device joined to on-premises Active Directory and also registered in Entra ID, so it can use device-based Conditional Access.">Hybrid join</span> has always depended on plumbing: Entra Connect syncing computer objects and a service connection point, or AD FS in federated tenants. Devices often sat in a pending state until the next sync cycle, which confused users and slowed down rollouts.</p>
<figure class="dg dg-compare-wrap"><div class="dg-compare" style="--cols:2"><div class="dg-col"><h4>Traditional hybrid join</h4><p class="dg-sub">Sync or federation</p><ul><li>Computer object synced by Entra Connect</li><li>Service connection point in AD</li><li>Device pending until the next sync</li><li>Or AD FS in federated tenants</li></ul></div><div class="dg-col t-ok"><h4>Entra Kerberos preview</h4><p class="dg-sub">At provisioning time</p><ul><li>No device sync required</li><li>No AD FS required</li><li>Joined during provisioning</li><li>Device-based Conditional Access works immediately</li></ul></div></div><figcaption>Two routes to a hybrid joined device.</figcaption></figure>

<p>A new preview lets devices become hybrid joined <b>at provisioning time</b> using Entra <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="kerberos" data-term="Kerberos" data-def="The ticket-based authentication protocol used by Active Directory. Entra ID can issue Kerberos tickets for some on-premises scenarios.">Kerberos</span>, without Entra Connect device sync or AD FS.</p>

<h2 id="why-it-s-interesting">Why it's interesting</h2>
<ul>
  <li><b>No waiting for sync.</b> The device is hybrid joined when it's provisioned, so device-based <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="conditional-access" data-term="Conditional Access" data-def="Entra ID&#39;s policy engine. Each policy says: if these users sign in to these apps under these conditions, then require (or block) something, such as MFA or a compliant device." data-post="/posts/conditional-access-report-only-and-what-if/">Conditional Access</span> works straight away.</li>
  <li><b>Less infrastructure.</b> It removes one more dependency on <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="connect-sync" data-term="Entra Connect Sync" data-def="The traditional server-based tool that syncs Active Directory objects to Entra ID, with configuration held on the server." data-post="/posts/connect-sync-to-cloud-sync/">Connect Sync</span>, in line with Microsoft's move to <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="cloud-sync" data-term="Entra Cloud Sync" data-def="Microsoft&#39;s lightweight agent-based service for syncing users and groups from Active Directory to Entra ID, configured in the cloud." data-post="/posts/connect-sync-to-cloud-sync/">Cloud Sync</span>.</li>
  <li><b>Fewer pending devices.</b> Hybrid join troubleshooting often comes down to sync timing and SCP configuration. This sidesteps most of it.</li>
</ul>
<h2 id="why-traditional-hybrid-join-is-slow">Why traditional hybrid join is slow</h2>
<figure class="dg dg-seq-wrap"><div class="dg-scroll"><svg class="dg-seq" viewBox="0 0 740 398" width="740" height="398" role="img" aria-label="Sequence diagram: Device, Active Directory, Entra Connect, Entra ID"><line class="life" x1="107.5" y1="46" x2="107.5" y2="390"/><rect class="actor" x="30" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="107.5" y="28" text-anchor="middle">Device</text><line class="life" x1="282.5" y1="46" x2="282.5" y2="390"/><rect class="actor" x="205" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="282.5" y="28" text-anchor="middle">Active Directory</text><line class="life" x1="457.5" y1="46" x2="457.5" y2="390"/><rect class="actor" x="380" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="457.5" y="28" text-anchor="middle">Entra Connect</text><line class="life" x1="632.5" y1="46" x2="632.5" y2="390"/><rect class="actor" x="555" y="6" width="155" height="34" rx="8"/><text class="actor-t" x="632.5" y="28" text-anchor="middle">Entra ID</text><text class="lbl" x="195" y="84" text-anchor="middle">Domain join, reads the</text><text class="lbl" x="195" y="99" text-anchor="middle">service connection</text><text class="lbl" x="195" y="114" text-anchor="middle">point</text><text class="num" x="115.5" y="124" text-anchor="start">1</text><line class="arrow" x1="107.5" y1="129" x2="273.5" y2="129"/><polygon class="head" points="282.5,129 272.5,124 272.5,134"/><text class="lbl" x="370" y="164" text-anchor="middle">Registers itself: device shows as pending</text><text class="num" x="115.5" y="174" text-anchor="start">2</text><line class="arrow t-accent" x1="107.5" y1="179" x2="623.5" y2="179"/><polygon class="head t-accent" points="632.5,179 622.5,174 622.5,184"/><text class="lbl" x="370" y="214" text-anchor="middle">Computer object waits</text><text class="lbl" x="370" y="229" text-anchor="middle">for the next sync cycle</text><text class="num" x="290.5" y="239" text-anchor="start">3</text><line class="arrow" x1="282.5" y1="244" x2="448.5" y2="244"/><polygon class="head" points="457.5,244 447.5,239 447.5,249"/><text class="lbl" x="545" y="279" text-anchor="middle">Synced object completes</text><text class="lbl" x="545" y="294" text-anchor="middle">the registration</text><text class="num" x="465.5" y="304" text-anchor="start">4</text><line class="arrow t-ok" x1="457.5" y1="309" x2="623.5" y2="309"/><polygon class="head t-ok" points="632.5,309 622.5,304 622.5,314"/><text class="lbl" x="370" y="344" text-anchor="middle">Next sign-in: device gets its primary refresh token</text><text class="num" x="115.5" y="354" text-anchor="start">5</text><line class="arrow t-ok" x1="107.5" y1="359" x2="623.5" y2="359"/><polygon class="head t-ok" points="632.5,359 622.5,354 622.5,364"/></svg></div><figcaption>The classic hybrid join path, and where devices get stuck as pending.</figcaption></figure>
<p>Every step in that chain is a place for something to go wrong: the computer object outside the sync scope, a missing or wrong SCP, a sync cycle that hasn't run yet. Most hybrid join tickets come down to one of them.</p>

<h2 id="should-you-use-it">Should you use it?</h2>
<p>For new Windows deployments, the first question is still whether you need hybrid join at all. <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="entra-join" data-term="Entra join" data-def="A corporate Windows device joined directly to Entra ID, with no on-premises domain membership needed.">Entra join</span> with Intune, and cloud Kerberos trust for access to on-premises resources, covers most needs. Where hybrid join is still required, such as apps or policies that depend on domain membership, this preview is worth testing in a lab now.</p>
<figure class="dg dg-decision-wrap"><div class="dg-decision"><div class="dg-q">Does the device need to be domain joined?</div><div class="dg-branches"><div class="dg-branch t-ok"><span class="dg-if">No: users only need on-prem file shares or apps</span><span class="dg-then">Entra join with Intune, plus cloud Kerberos trust</span></div><div class="dg-branch t-accent"><span class="dg-if">Yes: GPOs, domain-only apps or legacy tooling</span><span class="dg-then">Hybrid join. Test the Entra Kerberos preview for new builds</span></div><div class="dg-branch"><span class="dg-if">Not sure</span><span class="dg-then">Pilot Entra join with a small team and see what breaks</span></div></div></div><figcaption>Do you need hybrid join at all?</figcaption></figure>
<details class="faq"><summary>Will this replace my existing hybrid joined devices?</summary><p>No. It's about how new devices get joined. Existing devices carry on as they are.</p></details>
<details class="faq"><summary>Can I use it in production?</summary><p>It's a preview. Test it in a lab and watch for general availability before relying on it.</p></details>
<h2 id="what-to-test-in-a-lab">What to test in a lab</h2>
<ul class="check"><li>A new device built by your normal provisioning process ends up hybrid joined, not pending</li><li>Device-based Conditional Access works at the first user sign-in</li><li>Group Policy, domain-only apps and file shares still behave as expected</li><li>The device appears correctly in Intune if it's co-managed or enrolled</li><li>Your existing hybrid joined devices are unaffected</li></ul>

<div class="sources"><b>Sources</b><ul><li><a href="https://learn.microsoft.com/en-us/entra/fundamentals/whats-new" target="_blank" rel="noopener">Microsoft Learn: What's new in Microsoft Entra</a></li></ul></div>
]]></content:encoded>
</item>
<item>
<title>Clean up guest accounts with access reviews</title>
<link>https://azureblog.co.uk/posts/guest-access-reviews/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/guest-access-reviews/</guid>
<pubDate>Wed, 25 Mar 2026 08:00:00 GMT</pubDate>
<category>Entra ID</category><category>Governance</category>
<description>Guest accounts pile up quietly. Access reviews ask the right people whether each guest still needs access, and can remove them automatically if nobody says yes.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/guest-access-reviews.png" alt=""></p><p>Every Teams invite and shared document can leave a guest account behind. A few years on, most tenants have hundreds or thousands of guests, many with access nobody remembers granting.</p>
<figure class="dg dg-seq-wrap"><div class="dg-scroll"><svg class="dg-seq" viewBox="0 0 670 396" width="670" height="396" role="img" aria-label="Sequence diagram: Access reviews, Group owner, Guest account"><line class="life" x1="125" y1="46" x2="125" y2="388"/><rect class="actor" x="30" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="125" y="28" text-anchor="middle">Access reviews</text><line class="life" x1="335" y1="46" x2="335" y2="388"/><rect class="actor" x="240" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="335" y="28" text-anchor="middle">Group owner</text><line class="life" x1="545" y1="46" x2="545" y2="388"/><rect class="actor" x="450" y="6" width="190" height="34" rx="8"/><text class="actor-t" x="545" y="28" text-anchor="middle">Guest account</text><text class="lbl" x="230" y="84" text-anchor="middle">Review starts: approve or</text><text class="lbl" x="230" y="99" text-anchor="middle">deny each guest</text><text class="num" x="133" y="109" text-anchor="start">1</text><line class="arrow" x1="125" y1="114" x2="326" y2="114"/><polygon class="head" points="335,114 325,109 325,119"/><text class="lbl" x="230" y="149" text-anchor="middle">Approves 8, denies 3</text><text class="num" x="327" y="159" text-anchor="end">2</text><line class="arrow" x1="335" y1="164" x2="134" y2="164"/><polygon class="head" points="125,164 135,159 135,169"/><rect class="note t-accent" x="4" y="187" width="250" height="44" rx="6"/><text class="lbl" x="129" y="205" text-anchor="middle">Two-week window ends; no</text><text class="lbl" x="129" y="220" text-anchor="middle">response for 2 guests</text><text class="lbl" x="335" y="278" text-anchor="middle">Removes denied and unanswered guests from the group</text><text class="num" x="133" y="288" text-anchor="start">4</text><line class="arrow t-bad" x1="125" y1="293" x2="536" y2="293"/><polygon class="head t-bad" points="545,293 535,288 535,298"/><rect class="note t-ok" x="4" y="316" width="250" height="29" rx="6"/><text class="lbl" x="129" y="334" text-anchor="middle">Results recorded for audit</text></svg></div><figcaption>A quarterly guest review with auto-apply.</figcaption></figure>

<h2 id="what-an-access-review-does">What an access review does</h2>
<p>An <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="access-review" data-term="Access review" data-def="An Entra ID Governance feature that asks reviewers to confirm whether people still need access to a group, app or role, and can remove them automatically.">access review</span> asks reviewers to confirm whether each member of a group, team or app still needs access. You choose who reviews (group owners, managers, or the guests themselves), how often, and what happens to people nobody approves.</p>

<h2 id="a-good-guest-review">A good guest review</h2>
<ul>
  <li><b>Scope:</b> all Microsoft 365 groups and Teams with guest members, guests only.</li>
  <li><b>Reviewers:</b> group owners, since they know who they work with. Use a fallback reviewer for groups without owners.</li>
  <li><b>Recurrence:</b> quarterly, with a two-week window.</li>
  <li><b>If reviewers don't respond:</b> remove access. Without this, a review that nobody completes changes nothing.</li>
  <li><b>Auto-apply results</b>, so removals happen without anyone clicking a button.</li>
</ul>

<h2 id="go-one-step-further">Go one step further</h2>
<p>Removing a guest from a group doesn't delete their account. A separate review of <b>all guest users</b> in the tenant can block sign-in for guests who are denied, then delete them after a set period, such as 30 days. That stops dormant guest accounts building up.</p>
<figure class="dg dg-timeline-wrap"><ol class="dg-tl"><li class="t-bad"><span class="dg-dot"></span><time>Review ends</time><span>Guest denied</span></li><li class=""><span class="dg-dot"></span><time>Immediately</time><span>Sign-in blocked</span></li><li class="t-bad"><span class="dg-dot"></span><time>+30 days</time><span>Account deleted</span></li></ol><figcaption>What happens to a guest who isn't approved, using the all-guests review.</figcaption></figure>

<h2 id="licensing">Licensing</h2>
<p>Access reviews need Entra ID Governance licensing (or P2 for some scenarios). Reviewing guests uses a separate Entra ID Governance guest billing meter, which needs a linked Azure subscription.</p>
<div class="callout"><strong>Tell owners first:</strong> a short message explaining why they're getting review emails dramatically improves completion rates.</div>
<h2 id="making-reviews-easy-for-owners">Making reviews easy for owners</h2>
<ul>
  <li><b>Use recommendations.</b> Reviews can show a recommendation based on recent sign-in activity, which helps owners decide quickly.</li>
  <li><b>Keep the window realistic.</b> Two weeks with reminders works better than a few days.</li>
  <li><b>Explain the email.</b> Owners often ignore review emails they don't recognise. A short Teams post first makes a big difference.</li>
</ul>
<h2 id="measuring-the-effect">Measuring the effect</h2>
<p>Track the number of guests and the percentage that are stale (no sign-in for 90 days) before and after the first few reviews. The <a href="https://azureblog.co.uk/tools/health/">tenant health check</a> reports both.</p>

]]></content:encoded>
</item>
<item>
<title>Synced passkeys and passkey profiles are now GA in Entra ID</title>
<link>https://azureblog.co.uk/posts/synced-passkeys-and-passkey-profiles/</link>
<guid isPermaLink="true">https://azureblog.co.uk/posts/synced-passkeys-and-passkey-profiles/</guid>
<pubDate>Wed, 18 Mar 2026 08:00:00 GMT</pubDate>
<category>Entra ID</category><category>Passkeys</category><category>What&#39;s new</category>
<description>Entra ID now supports passkeys stored in password managers and phone platforms, and lets you set different passkey rules for different groups. Here&#39;s how to use both sensibly.</description>
<content:encoded><![CDATA[<p><img src="https://azureblog.co.uk/og/synced-passkeys-and-passkey-profiles.png" alt=""></p><p>Two <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="passkey" data-term="Passkey" data-def="A FIDO2 credential made of a key pair. The private key stays on the device or in a password manager and only signs in to the site it was created for.">passkey</span> features reached general availability together, and they're designed to be used as a pair.</p>
<figure class="dg dg-compare-wrap"><div class="dg-compare" style="--cols:2"><div class="dg-col t-ok"><h4>Device-bound passkey</h4><p class="dg-sub">security key, Authenticator, Windows Hello</p><ul><li>The key never leaves that device</li><li>Attestation can confirm the exact model</li><li>Losing the device means registering again</li></ul></div><div class="dg-col t-accent"><h4>Synced passkey</h4><p class="dg-sub">stored by a passkey provider</p><ul><li>Available across the user's devices</li><li>Survives a lost or replaced phone</li><li>Less control over where the key lives</li></ul></div></div><figcaption>The trade-off between the two passkey types.</figcaption></figure>

<h2 id="synced-passkeys">Synced passkeys</h2>
<p>Until now, Entra passkeys were <b>device-bound</b>: tied to one security key or one phone's authenticator. <b>Synced passkeys</b> live in a passkey provider, such as the built-in platform provider on a phone or a third-party password manager, and are available across all of a user's devices.</p>
<p>The trade-off is simple. Synced passkeys are far easier for users: lose a phone, and the passkey is still there on the next device. Device-bound passkeys give you stronger guarantees about exactly where the key lives.</p>

<h2 id="passkey-profiles">Passkey profiles</h2>
<p>Passkey profiles let you define several sets of passkey rules in the authentication methods policy and target each at different groups. Each profile controls:</p>
<ul>
  <li>which passkey types are allowed (device-bound, synced, or both)</li>
  <li>whether attestation is enforced</li>
  <li>which authenticators are allowed or blocked</li>
</ul>
<p>If you already had passkeys configured, those settings moved into a default profile automatically. Later updates raised the limit to 10 profiles per tenant and gave the passkey policy its own storage allowance.</p>

<h2 id="a-sensible-starting-design">A sensible starting design</h2>
<div class="tablewrap"><table>
  <tr><th>Profile</th><th>Who</th><th>Rules</th></tr>
  <tr><td>Admins</td><td>Privileged role holders</td><td>Device-bound only, attestation enforced, approved security keys or Authenticator</td></tr>
  <tr><td>Standard</td><td>Everyone else</td><td>Device-bound or synced, no attestation</td></tr>
</table></div>
<p>This gets most users onto <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="phishing-resistant" data-term="Phishing-resistant MFA" data-def="Sign-in methods bound to the real site, so they can&#39;t be relayed by a fake one: passkeys (FIDO2), Windows Hello for Business and multifactor certificate-based authentication." data-post="/posts/authentication-strengths-explained/">phishing-resistant</span> sign-in with very little friction, while keeping a tighter grip on the accounts attackers want most.</p>
<div class="callout"><strong>Pair it with Conditional Access:</strong> once users have passkeys, require the Phishing-resistant <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="mfa" data-term="MFA" data-def="Multifactor authentication: proving who you are with more than one factor, such as something you know, something you have, or something you are.">MFA</span> <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="authentication-strength" data-term="Authentication strength" data-def="A Conditional Access grant control that requires specific sign-in methods, such as phishing-resistant MFA, rather than any MFA method." data-post="/posts/authentication-strengths-explained/">authentication strength</span> for admin portals and sensitive apps.</div>
<h2 id="how-entra-decides-which-rules-apply">How Entra decides which rules apply</h2>
<p>Each passkey profile is targeted at groups. When a user registers or uses a passkey, Entra applies the profile assigned to them. Keep the targeting simple: one restrictive profile for privileged users, one relaxed profile for everyone else, and a clear rule for who goes where. Profiles that overlap in unclear ways become hard to support.</p>
<figure class="dg dg-layers-wrap"><div class="dg-layers"><div class="dg-layer" style="--depth:0"><div class="dg-lh"><b>Authentication methods policy</b><span>Passkeys (FIDO2) enabled</span></div><div class="dg-layer" style="--depth:1"><div class="dg-lh"><b>Passkey profile: Admins</b><span>Device-bound only, attestation enforced</span></div><div class="dg-layer" style="--depth:2"><div class="dg-lh"><b>Users in the admin group</b><span>Can register approved keys or Authenticator only</span></div></div></div></div></div><figcaption>How profiles narrow down what's allowed.</figcaption></figure>
<h2 id="rolling-it-out">Rolling it out</h2>
<ol>
  <li>Check what your existing passkey settings migrated into. Tenants that already had passkeys configured get a default profile with those settings.</li>
  <li>Create the admin profile first and test it with two or three admins, including registration on a new device.</li>
  <li>Widen the standard profile to allow synced passkeys, then run a registration campaign.</li>
  <li>Once most users have a passkey, require the <a href="https://azureblog.co.uk/posts/authentication-strengths-explained/">phishing-resistant MFA</a> authentication strength for sensitive apps.</li>
</ol>

<h2 id="checklist">Checklist</h2>
<ul class="check"><li>Decide which groups may use synced passkeys</li><li>Create an admin profile with device-bound keys and attestation</li><li>Test registration and sign-in on Windows, iOS and Android</li><li>Brief the service desk on lost-device recovery with a <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="tap" data-term="Temporary Access Pass" data-def="A time-limited passcode issued by an admin, used to sign in and register passwordless methods without ever having a password." data-post="/posts/temporary-access-pass-onboarding/">Temporary Access Pass</span></li><li>Add <span class="gl" tabindex="0" role="button" aria-expanded="false" data-gl="conditional-access" data-term="Conditional Access" data-def="Entra ID&#39;s policy engine. Each policy says: if these users sign in to these apps under these conditions, then require (or block) something, such as MFA or a compliant device." data-post="/posts/conditional-access-report-only-and-what-if/">Conditional Access</span> authentication strengths once adoption is high</li></ul>

<div class="sources"><b>Sources</b><ul><li><a href="https://learn.microsoft.com/en-us/entra/identity/authentication/how-to-authentication-passkeys-fido2" target="_blank" rel="noopener">Microsoft Learn: Enable passkeys (FIDO2) in Entra ID</a></li><li><a href="https://learn.microsoft.com/en-us/entra/fundamentals/whats-new" target="_blank" rel="noopener">Microsoft Learn: What's new in Microsoft Entra</a></li></ul></div>
]]></content:encoded>
</item>
</channel>
</rss>
