<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Entra ID on BuiltWithCaffeine</title><link>https://blog.builtwithcaffeine.cloud/categories/entra-id/</link><description>Recent content in Entra ID on BuiltWithCaffeine</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><copyright>- BuiltWithCaffeine</copyright><lastBuildDate>Tue, 28 Jul 2026 10:00:00 +0000</lastBuildDate><atom:link href="https://blog.builtwithcaffeine.cloud/categories/entra-id/rss.xml" rel="self" type="application/rss+xml"/><item><title>MSGraph - Custom User Module - Including B2B Guest Accounts</title><link>https://blog.builtwithcaffeine.cloud/posts/msgraph-bicep-guest-user-lookup/</link><pubDate>Tue, 28 Jul 2026 10:00:00 +0000</pubDate><guid>https://blog.builtwithcaffeine.cloud/posts/msgraph-bicep-guest-user-lookup/</guid><description>&lt;h2 id="the-back-story"&gt;The Back Story
&lt;/h2&gt;&lt;p&gt;If you&amp;rsquo;ve spent any time wiring up Microsoft Entra ID resources with the &lt;a class="link" href="https://learn.microsoft.com/en-us/graph/templates/bicep/overview?view=graph-bicep-1.0" target="_blank" rel="noopener"
&gt;Microsoft Graph Bicep extension&lt;/a&gt;, you&amp;rsquo;ll have run into &lt;code&gt;Microsoft.Graph/users&lt;/code&gt;. It&amp;rsquo;s documented here:&lt;/p&gt;
&lt;style type="text/css"&gt;
.box-shortcode {
display: flex;
gap: 0.75em;
padding: 1.6em;
padding-top: 1.4em;
line-height: 1.6;
margin: 1em 0 2em;
border-radius: 8px;
color: #000;
background: #f3ebe850;
align-items: flex-start;
}
[data-scheme="dark"] .box-shortcode {
color: #fff;
background: #333;
}
.box-shortcode.warning { background: #ff6b6b4f; }
.box-shortcode.info { background: #0089e41c; box-shadow: 3px 3px 5px #0089e410; }
.box-shortcode.important { background: #f7ec2c7d; }
.box-shortcode.tip { background: #a3ffa34d; box-shadow: 3px 3px 5px #0089e410; }
.icon-box {
display: inline-flex;
flex: 0 0 auto;
line-height: 1;
align-self: flex-start;
margin-top: 0.2em;
}
.icon-box svg {
height: 1.2em;
width: 1.2em;
fill: currentColor;
}
.box-shortcode .box-body {
flex: 1 1 auto;
min-width: 0;
}
.box-shortcode .box-body &gt; :first-child {
margin-top: 0;
}
.box-shortcode .box-body &gt; :last-child {
margin-bottom: 0;
}
.box-shortcode .sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.box-shortcode :not(pre) &gt; code {
border-radius: 4px;
padding: 0.15em 0.45em;
background: #f1f3f5;
border: 1px solid #e0e4e8;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
font-size: 0.85em;
line-height: 1.25em;
white-space: normal;
display: inline-block;
vertical-align: baseline;
}
.box-shortcode p &gt; code,
.box-shortcode li &gt; code,
.box-shortcode span &gt; code,
.box-shortcode div &gt; code,
.box-shortcode em &gt; code,
.box-shortcode strong &gt; code {
background: #f1f3f5;
border: 1px solid #e0e4e8;
padding: 0.15em 0.45em;
border-radius: 4px;
font-weight: 600;
}
[data-scheme="dark"] .box-shortcode :not(pre) &gt; code {
background: #2d333b;
border-color: #3b424b;
color: #e6edf3;
}
[data-scheme="dark"] .box-shortcode p &gt; code,
[data-scheme="dark"] .box-shortcode li &gt; code,
[data-scheme="dark"] .box-shortcode span &gt; code,
[data-scheme="dark"] .box-shortcode div &gt; code,
[data-scheme="dark"] .box-shortcode em &gt; code,
[data-scheme="dark"] .box-shortcode strong &gt; code {
background: #2d333b;
border-color: #3b424b;
color: #e6edf3;
font-weight: 600;
}
.box-shortcode strong,
.box-shortcode strong code,
.box-shortcode p strong code,
.box-shortcode li strong code {
font-weight: 600 !important;
}
.box-shortcode pre,
.box-shortcode .highlight {
margin: 1em auto;
overflow: hidden;
display: block;
border-radius: 6px;
box-sizing: border-box;
position: relative;
isolation: isolate;
max-width: 100%;
}
.box-shortcode .highlight {
padding-left: 0;
padding-right: 0;
margin-right: 0;
width: 100%;
max-width: 100%;
}
@media (min-width: 1100px) {
.box-shortcode pre,
.box-shortcode .highlight {
max-width: 900px;
}
}
.box-shortcode pre code,
.box-shortcode .highlight pre,
.box-shortcode .highlight code {
display: block;
border-radius: inherit;
background-clip: padding-box;
margin: 0;
}
.box-shortcode .highlight-wrapper {
margin: 1em auto;
border-radius: 6px;
overflow: hidden;
box-sizing: border-box;
isolation: isolate;
max-width: 100%;
}
@media (min-width: 1100px) {
.box-shortcode .highlight-wrapper {
max-width: 900px;
}
}
.box-shortcode .highlight-wrapper .highlight {
border-radius: inherit;
overflow: hidden;
}
.box-shortcode .highlight button.copy {
position: absolute;
top: 0.5em;
right: 0.5em;
z-index: 1;
border-radius: 4px;
}
&lt;/style&gt;
&lt;svg width="0" height="0" display="none" xmlns="http://www.w3.org/2000/svg"&gt;
&lt;symbol id="tip-box" viewBox="0 0 512 512" preserveAspectRatio="xMidYMid meet"&gt;
&lt;path
d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"/&gt;
&lt;/symbol&gt;
&lt;symbol id="important-box" viewBox="0 0 512 512" preserveAspectRatio="xMidYMid meet"&gt;
&lt;path
d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"/&gt;
&lt;/symbol&gt;
&lt;symbol id="warning-box" viewBox="0 0 576 512" preserveAspectRatio="xMidYMid meet"&gt;
&lt;path
d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"/&gt;
&lt;/symbol&gt;
&lt;symbol id="info-box" viewBox="0 0 512 512" preserveAspectRatio="xMidYMid meet"&gt;
&lt;path
d="M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z"/&gt;
&lt;/symbol&gt;
&lt;/svg&gt;&lt;div class="box box-shortcode info" data-variant="info" role="status"&gt;
&lt;span class="icon-box"&gt;
&lt;span class="sr-only"&gt;Info&lt;/span&gt;
&lt;svg&gt;&lt;use href="#info-box"&gt;&lt;/use&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="box-body"&gt;
&lt;p&gt;&lt;a class="link" href="https://learn.microsoft.com/en-us/graph/templates/bicep/reference/users?view=graph-bicep-1.0" target="_blank" rel="noopener"
&gt;Microsoft.Graph users reference (graph-bicep-1.0)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Microsoft.Graph/users&lt;/code&gt; is a &lt;strong&gt;read-only&lt;/strong&gt; resource type - &lt;code&gt;x-bicep-resource-writable-scopes: None&lt;/code&gt;. It can only ever be declared with the &lt;code&gt;existing&lt;/code&gt; keyword; Bicep will never create or modify a user through this resource type.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;That read-only nature makes it perfect for exactly one job: resolving an existing user&amp;rsquo;s object ID (and a handful of profile properties) by User Principal Name, so you can feed that ID into something else - most commonly the &lt;code&gt;ownerIds&lt;/code&gt;/&lt;code&gt;memberIds&lt;/code&gt; parameters of a &lt;code&gt;Microsoft.Graph/groups&lt;/code&gt; module, without ever hardcoding a GUID in your Bicep files.&lt;/p&gt;
&lt;p&gt;It works brilliantly&amp;hellip; right up until one of those users is a B2B guest instead of a native member. That&amp;rsquo;s where I hit a wall, and where this post picks up.&lt;/p&gt;
&lt;h2 id="the-problem"&gt;The Problem
&lt;/h2&gt;&lt;p&gt;Working for a managed service provider (MSP) sometimes means most of your Bicep deployments run against customer tenants rather than your own - typically authenticated as an admin account that&amp;rsquo;s been invited into each customer tenant as a Microsoft Entra B2B guest. When I first wired up a simple user-lookup module for exactly this scenario, I passed in the account&amp;rsquo;s normal email address as the &lt;code&gt;userPrincipalName&lt;/code&gt; - and the lookup failed every time.&lt;/p&gt;
&lt;p&gt;The reason is how Entra ID handles B2B guest sign-in names. When you invite &lt;code&gt;jane.doe@contoso.com&lt;/code&gt; into &lt;code&gt;fabrikam.onmicrosoft.com&lt;/code&gt; as a guest, Entra ID doesn&amp;rsquo;t store the UPN as-is. It rewrites it into a mangled form:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;jane.doe_contoso.com#EXT#@fabrikam.onmicrosoft.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The pattern is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Take the invited email address.&lt;/li&gt;
&lt;li&gt;Replace the &lt;code&gt;@&lt;/code&gt; with &lt;code&gt;_&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Append &lt;code&gt;#EXT#@&amp;lt;resource tenant's initial/verified domain&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It&amp;rsquo;s a completely deterministic string transform - no directory lookup is required to compute it - but Bicep has no way of &lt;em&gt;knowing&lt;/em&gt; whether a given address belongs to a guest or a member in the target tenant. That&amp;rsquo;s a fact about the tenant&amp;rsquo;s directory, not something you can derive from the string itself. So the caller has to tell it.&lt;/p&gt;
&lt;h2 id="bicep-config-configuration"&gt;Bicep Config Configuration
&lt;/h2&gt;&lt;p&gt;Before any of the module code below will resolve, Bicep needs to know what &lt;code&gt;microsoftGraphv1&lt;/code&gt; actually refers to. That mapping doesn&amp;rsquo;t live in the module itself - it&amp;rsquo;s declared once in a &lt;code&gt;bicepconfig.json&lt;/code&gt; file sitting alongside your Bicep files (or in a parent folder).&lt;/p&gt;
&lt;p&gt;Per the &lt;a class="link" href="https://learn.microsoft.com/en-us/graph/templates/bicep/quickstart-create-bicep-interactive-mode?view=graph-bicep-1.0&amp;amp;tabs=cli" target="_blank" rel="noopener"
&gt;official quickstart&lt;/a&gt;, add an &lt;code&gt;extensions&lt;/code&gt; block that pins the alias to the Microsoft Graph extension package published to the Microsoft Artifact Registry:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;extensions&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;microsoftGraphv1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;br:mcr.microsoft.com/bicep/extensions/microsoftgraph/v1.0:1.0.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;br&gt;
&lt;div class="box box-shortcode important" data-variant="important" role="note"&gt;
&lt;span class="icon-box"&gt;
&lt;span class="sr-only"&gt;Important&lt;/span&gt;
&lt;svg&gt;&lt;use href="#important-box"&gt;&lt;/use&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="box-body"&gt;
&lt;p&gt;The key on the left (&lt;code&gt;microsoftGraphv1&lt;/code&gt; here) is just a friendly alias - it has to match, character-for-character, whatever name you use in the &lt;code&gt;extension microsoftGraphv1&lt;/code&gt; statement at the top of your Bicep file. Get the casing out of sync between &lt;code&gt;bicepconfig.json&lt;/code&gt; and your &lt;code&gt;.bicep&lt;/code&gt; file and Bicep will fail to resolve the extension.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;A couple of other things worth checking before you start:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Bicep CLI version&lt;/strong&gt; &lt;br&gt;
You&amp;rsquo;ll need at least &lt;code&gt;v0.36.1&lt;/code&gt; for Microsoft Graph extension support. &lt;br&gt;
Run &lt;code&gt;az bicep version&lt;/code&gt; (or &lt;code&gt;bicep --version&lt;/code&gt; if you&amp;rsquo;re using the standalone CLI) and upgrade with &lt;code&gt;az bicep upgrade&lt;/code&gt; if needed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;VS Code Bicep extension&lt;/strong&gt; &lt;br&gt;
Install/update the &lt;a class="link" href="https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-bicep" target="_blank" rel="noopener"
&gt;Bicep extension&lt;/a&gt; so IntelliSense picks up the Microsoft Graph types once &lt;code&gt;bicepconfig.json&lt;/code&gt; is in place.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Permissions&lt;/strong&gt; &lt;br&gt;
Per the &lt;a class="link" href="https://learn.microsoft.com/en-us/graph/templates/bicep/reference/users?view=graph-bicep-1.0" target="_blank" rel="noopener"
&gt;&lt;code&gt;Microsoft.Graph/users&lt;/code&gt; reference&lt;/a&gt;, reading an &lt;code&gt;existing&lt;/code&gt; user needs at least &lt;code&gt;User.Read&lt;/code&gt; for a signed-in (delegated) identity, or &lt;code&gt;User.Read.All&lt;/code&gt; for an application/service principal identity. Higher-privileged scopes like &lt;code&gt;User.ReadWrite.All&lt;/code&gt; or &lt;code&gt;Directory.Read.All&lt;/code&gt; also work, but aren&amp;rsquo;t required just for lookups.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With &lt;code&gt;bicepconfig.json&lt;/code&gt; in place, &lt;code&gt;extension microsoftGraphv1&lt;/code&gt; at the top of the module resolves correctly and the &lt;code&gt;Microsoft.Graph/users@v1.0&lt;/code&gt; resource type becomes available.&lt;/p&gt;
&lt;h2 id="the-solution"&gt;The Solution
&lt;/h2&gt;&lt;p&gt;I extended the plain UPN-lookup pattern into a small reusable module that accepts an &lt;code&gt;isGuest&lt;/code&gt; flag per user, and reconstructs the mangled UPN automatically when needed.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;span class="lnt"&gt;44
&lt;/span&gt;&lt;span class="lnt"&gt;45
&lt;/span&gt;&lt;span class="lnt"&gt;46
&lt;/span&gt;&lt;span class="lnt"&gt;47
&lt;/span&gt;&lt;span class="lnt"&gt;48
&lt;/span&gt;&lt;span class="lnt"&gt;49
&lt;/span&gt;&lt;span class="lnt"&gt;50
&lt;/span&gt;&lt;span class="lnt"&gt;51
&lt;/span&gt;&lt;span class="lnt"&gt;52
&lt;/span&gt;&lt;span class="lnt"&gt;53
&lt;/span&gt;&lt;span class="lnt"&gt;54
&lt;/span&gt;&lt;span class="lnt"&gt;55
&lt;/span&gt;&lt;span class="lnt"&gt;56
&lt;/span&gt;&lt;span class="lnt"&gt;57
&lt;/span&gt;&lt;span class="lnt"&gt;58
&lt;/span&gt;&lt;span class="lnt"&gt;59
&lt;/span&gt;&lt;span class="lnt"&gt;60
&lt;/span&gt;&lt;span class="lnt"&gt;61
&lt;/span&gt;&lt;span class="lnt"&gt;62
&lt;/span&gt;&lt;span class="lnt"&gt;63
&lt;/span&gt;&lt;span class="lnt"&gt;64
&lt;/span&gt;&lt;span class="lnt"&gt;65
&lt;/span&gt;&lt;span class="lnt"&gt;66
&lt;/span&gt;&lt;span class="lnt"&gt;67
&lt;/span&gt;&lt;span class="lnt"&gt;68
&lt;/span&gt;&lt;span class="lnt"&gt;69
&lt;/span&gt;&lt;span class="lnt"&gt;70
&lt;/span&gt;&lt;span class="lnt"&gt;71
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bicep" data-lang="bicep"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;metadata&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Microsoft Graph User Lookup&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;metadata&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;description&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Resolves one or more existing Microsoft Entra users by User Principal Name (UPN) via the Microsoft Graph Bicep extension. Microsoft.Graph/users is a read-only resource type in the Bicep extension (writable scopes: None) - it can only be referenced with the `existing` keyword, never created or updated. This module is intended for looking up object IDs (and other profile attributes) of already-existing users, for example to feed the `ownerIds`/`memberIds` parameters of the microsoftGraph/groups module without hardcoding object IDs.&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;metadata&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;owner&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;extension&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;microsoftGraphv1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ================ //&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Types //&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ================ //&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;An identity to resolve to a Microsoft Entra user object.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;userLookup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Required. The UPN/email of the identity as known outside the target tenant (e.g. the address a B2B guest was invited with, or the native UPN for a member account).&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;userPrincipalName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Optional. Set to true when this identity was invited as a Microsoft Entra B2B guest into the target tenant rather than being a native member account. When true, Entra ID rewrites the sign-in UPN to the mangled `local-part_domain#EXT#@resourceTenantDomain` form, and `guestTenantDomain` is used to reconstruct it for the lookup. Defaults to false (native member).&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;isGuest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ================ //&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Parameters //&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ================ //&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Optional. The identities to look up in the target tenant. Leave empty if no lookups are needed.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;param&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;userLookup&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Optional. Required only when at least one entry in `users` has `isGuest: true`. The initial/verified domain of the target Microsoft Entra tenant (e.g. \&amp;#39;contoso.onmicrosoft.com\&amp;#39;), used to reconstruct the mangled UPN Entra ID assigns to B2B guest accounts.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;param&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;guestTenantDomain&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ================ //&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Variables //&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ================ //&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Microsoft Entra B2B guest UPN mangling convention: the invited email&amp;#39;s &amp;#39;@&amp;#39; is replaced with&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// &amp;#39;_&amp;#39;, then &amp;#39;#EXT#@&amp;lt;resourceTenantDomain&amp;gt;&amp;#39; is appended - e.g. &amp;#39;jane.doe@contoso.com&amp;#39; invited into&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// &amp;#39;fabrikam.onmicrosoft.com&amp;#39; signs in as &amp;#39;jane.doe_contoso.com#EXT#@fabrikam.onmicrosoft.com&amp;#39;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// This is a deterministic string transform (no directory lookup required), but Bicep cannot infer&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// on its own whether a given address is a guest or a member in the target tenant - that must be&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// declared by the caller via `isGuest`, since this is a fact about the tenant, not the UPN string.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;resolvedUserPrincipalNames&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.?&lt;/span&gt;&lt;span class="nv"&gt;isGuest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;??&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;userPrincipalName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;@&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;#EXT#@&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;guestTenantDomain&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;userPrincipalName&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ================ //&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Resources //&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ================ //&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// NOTE: Microsoft.Graph/users only supports `existing` (x-bicep-resource-flags: ReadOnly,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// x-bicep-resource-writable-scopes: None) - every other property on the resource is read-only,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// so `userPrincipalName` is the only value that can be supplied here. This does not create or&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// modify any user; it only resolves the UPN to the user&amp;#39;s directory object for output.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;resource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;userResources&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Microsoft.Graph/users@v1.0&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;existing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;resolvedUserPrincipalName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;resolvedUserPrincipalNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;userPrincipalName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;resolvedUserPrincipalName&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ================ //&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Outputs //&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ================ //&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;The object IDs of the resolved users, in the same order as users.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;userIds&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;users&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;userResources&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nv"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;The display names of the resolved users, in the same order as users.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;displayNames&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;users&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;userResources&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nv"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;The SMTP mail addresses of the resolved users, in the same order as users. May be empty for users without a mailbox.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;mails&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;users&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;userResources&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nv"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;br&gt;
&lt;h3 id="walking-through-it"&gt;Walking Through It
&lt;/h3&gt;&lt;p&gt;A few things worth calling out for anyone adapting this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;userLookup&lt;/code&gt; type&lt;/strong&gt; - each entry pairs the &lt;em&gt;invited&lt;/em&gt; UPN with an &lt;code&gt;isGuest&lt;/code&gt; flag. This keeps the caller-facing interface identical whether you&amp;rsquo;re resolving a native member or a guest; the mangling is entirely internal to the module.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;resolvedUserPrincipalNames&lt;/code&gt;&lt;/strong&gt; - a single &lt;code&gt;for&lt;/code&gt; expression does the mangling up front, using the null-coalescing &lt;code&gt;user.?isGuest ?? false&lt;/code&gt; pattern so &lt;code&gt;isGuest&lt;/code&gt; can be omitted entirely for native members.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;userResources&lt;/code&gt; existing loop&lt;/strong&gt; - this is still the same one-liner pattern as the official docs, just fed the &lt;em&gt;resolved&lt;/em&gt; (mangled where needed) UPNs instead of the raw ones.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Outputs are index-aligned&lt;/strong&gt; - &lt;code&gt;userIds&lt;/code&gt;, &lt;code&gt;displayNames&lt;/code&gt; and &lt;code&gt;mails&lt;/code&gt; all come back in the same order as the &lt;code&gt;users&lt;/code&gt; array you passed in, so you can safely zip them back together in the calling module.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="box box-shortcode important" data-variant="important" role="note"&gt;
&lt;span class="icon-box"&gt;
&lt;span class="sr-only"&gt;Important&lt;/span&gt;
&lt;svg&gt;&lt;use href="#important-box"&gt;&lt;/use&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="box-body"&gt;
&lt;p&gt;&lt;code&gt;guestTenantDomain&lt;/code&gt; only needs to be supplied once per module call, even if you&amp;rsquo;re resolving a mix of guest and member accounts in the same array - it&amp;rsquo;s applied only to entries where &lt;code&gt;isGuest&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;. Get the value wrong (e.g. use the &lt;em&gt;home&lt;/em&gt; tenant domain instead of the &lt;em&gt;resource&lt;/em&gt; tenant&amp;rsquo;s initial domain) and the lookup will silently fail to match, since Graph will simply not find a user with that mangled UPN.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id="using-it-resolving-our-deployment-identity"&gt;Using It: Resolving Our Deployment Identity
&lt;/h3&gt;&lt;p&gt;In an MSP-style deployment model, the automation account authenticates as an admin identity that is &lt;em&gt;always&lt;/em&gt; a B2B guest in whichever customer tenant it&amp;rsquo;s currently deploying into. Resolving its object ID (for example, to seed as the owner of a newly created Entra ID group) now looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bicep" data-lang="bicep"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;getEntraIdDeploymentUser&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;../modules/microsoftGraph/users/main.bicep&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;get-entra-id-deployment-user-&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;locationShortCode&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;resourceGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;resourceGroupNameArray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;userPrincipalName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;deployedBy&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;isGuest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Required if User is B2B Guest Invited&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;guestTenantDomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;entraGuestTenantDomain&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;dependsOn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;createResourceGroups&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;From here, &lt;code&gt;getEntraIdDeploymentUser.outputs.userIds[0]&lt;/code&gt; drops straight into an &lt;code&gt;ownerIds&lt;/code&gt;/&lt;code&gt;memberIds&lt;/code&gt; array on a downstream groups module - no manual object ID lookups, no copy-pasting GUIDs between the Entra admin center and a parameters file, and no drift when the deployment is re-run against a different customer tenant.&lt;/p&gt;
&lt;h2 id="why-this-matters"&gt;Why This Matters
&lt;/h2&gt;&lt;p&gt;The official &lt;code&gt;Microsoft.Graph/users&lt;/code&gt; pattern works great in a single-tenant world where everyone deploying is a native member. The moment you&amp;rsquo;re working as a managed service provider - where the identity running the deployment is a guest in every customer tenant you touch - the plain UPN lookup breaks silently, and it&amp;rsquo;s not obvious why unless you already know about Entra&amp;rsquo;s UPN mangling behaviour.&lt;/p&gt;
&lt;p&gt;Wrapping that mangling logic into the lookup module itself means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Callers keep passing in the UPN they actually know (the invited email address), not some Graph-internal representation of it.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;isGuest&lt;/code&gt;/&lt;code&gt;guestTenantDomain&lt;/code&gt; distinction is explicit and self-documenting at the call site.&lt;/li&gt;
&lt;li&gt;The module composes cleanly with the rest of a &lt;a class="link" href="https://blog.builtwithcaffeine.cloud/p/bicep-microsoft-graph-custom-modules/" &gt;Microsoft Graph Bicep module set&lt;/a&gt; - resolved user IDs feed straight into groups, app role assignments, or anything else that needs a &lt;code&gt;principalId&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="box box-shortcode tip" data-variant="tip" role="note"&gt;
&lt;span class="icon-box"&gt;
&lt;span class="sr-only"&gt;Tip&lt;/span&gt;
&lt;svg&gt;&lt;use href="#tip-box"&gt;&lt;/use&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="box-body"&gt;
&lt;p&gt;If your organisation only ever deploys as native members, you can drop &lt;code&gt;isGuest&lt;/code&gt;/&lt;code&gt;guestTenantDomain&lt;/code&gt; entirely and this collapses back to the plain official pattern - the &lt;code&gt;userLookup&lt;/code&gt; type defaults &lt;code&gt;isGuest&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt;, so existing callers don&amp;rsquo;t need to change.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;The official &lt;code&gt;Microsoft.Graph/users&lt;/code&gt; lookup pattern is simple and works well - as long as every account you&amp;rsquo;re resolving is a native member of the tenant you&amp;rsquo;re deploying into. That assumption breaks down the moment you&amp;rsquo;re working cross-tenant with guest accounts, which is the everyday reality for anyone deploying Bicep on behalf of a customer or partners rather than into their own home tenant.&lt;/p&gt;
&lt;p&gt;Wrapping the B2B UPN-mangling logic into the module itself means that reality doesn&amp;rsquo;t leak into every deployment that needs it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;You keep referencing people by the UPN you actually know - the address they were invited with - instead of hand-crafting the &lt;code&gt;local-part_domain#EXT#@resourceTenantDomain&lt;/code&gt; form yourself, or worse, hardcoding a resolved object ID that will break the moment the deployment targets a different tenant.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;isGuest&lt;/code&gt;/&lt;code&gt;guestTenantDomain&lt;/code&gt; parameters make the guest-vs-member distinction an explicit, self-documenting part of the module&amp;rsquo;s interface, rather than a tribal-knowledge gotcha that only bites you at deployment time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It composes cleanly with everything else in your Microsoft Graph Bicep estate - resolved object IDs plug straight into &lt;code&gt;ownerIds&lt;/code&gt;/&lt;code&gt;memberIds&lt;/code&gt; on a groups module, &lt;code&gt;principalId&lt;/code&gt; on a role assignment, or anywhere else a directory object reference is needed.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you only ever deploy as a native member, none of this changes anything for you - &lt;code&gt;isGuest&lt;/code&gt; defaults to &lt;code&gt;false&lt;/code&gt; and the module behaves exactly like the plain official pattern. But if guest identities are part of your day-to-day deployment story, this small extension turns a silent, confusing lookup failure into something that just works.&lt;/p&gt;</description></item></channel></rss>