Public mode

No access to your tenant or devices. Every item links to its source. What this means

Some sources or documents need attention: 258/260 feeds/APIs · 416/439 docs · checked 22:55 UTC Customize Public modeDiscuss ChangeIntel on Discord

Conditional Access target resources

This page's changes and edits · All guidance changes

Microsoft's edit ·

Merge pull request #11375 from juntakata/patch-2

Microsoft's commit message in MicrosoftDocs/entra-docs · commit 229ce3c · +110 −34 lines · also on GitHub

⋯ 172 unchanged lines
173173
174174 | Example scenario | User impact (before → after) | Conditional Access evaluation |
175175 |---|---|---|
176 −| A user signs into VSCode desktop client, which requests openid and profile scopes. | **Before**: User not prompted for MFA</br>**After**: User is prompted for MFA | Conditional Access is now evaluated using Windows Azure Active Directory as the enforcement audience. |
176 +| A user signs into Visual Studio Code desktop client, which requests openid and profile scopes. | **Before**: User not prompted for MFA</br>**After**: User is prompted for MFA | Conditional Access is now evaluated using Windows Azure Active Directory as the enforcement audience. |
177177 | A user signs in using Azure CLI, which requests only `User.Read`. | **Before**: User not prompted for MFA</br>**After**: User is prompted for MFA | Conditional Access is now evaluated using Windows Azure Active Directory as the enforcement audience. |
178178 | A user signs in through a confidential client application (excluded from the policy) that requests only `User.Read` and `People.Read`. | **Before**: User not prompted for MFA</br>**After**: User is prompted for MFA | Conditional Access is now evaluated using Windows Azure Active Directory as the enforcement audience. |
179179
⋯ 23 unchanged lines
203203
204204 ```powershell
205205 # ==============================
206 −# Inventory of tenant-owned apps whose delegated consent grants include ONLY
206 +# Inventory of apps whose delegated consent grants include ONLY
207207 # the OIDC scopes + specific directory scopes listed below.
208208 #
209 +# Enhancements incorporated:
210 +# - Supported both PowerShell 5.1 and 7.x
211 +# - Add user sign-in count (last 7 days) per app
212 +#
209213 # Output:
210214 # - ServicePrincipalObjectId (oauth2PermissionGrants.clientId = SP object id)
215 +# - AppId
216 +# - AppDisplayName
217 +# - AppOwnerOrganizationId (for classification)
211218 # - Scopes (union of delegated scopes granted)
219 +# - UserSigninsLast7Days (Successful + Failed)
212220 # ==============================
213221
214222 $TenantId = Read-Host "Enter your Microsoft Entra tenant ID (GUID)"
⋯ 6 unchanged lines
221229 )
222230
223231 Disconnect-MgGraph -ErrorAction SilentlyContinue
232 +
224233 Connect-MgGraph -TenantId $TenantId -Scopes @(
225234 "DelegatedPermissionGrant.Read.All",
226 − "Directory.Read.All"
235 + "Directory.Read.All",
236 + "Reports.Read.All"
227237 )
228238
239 +# ------------------------------
229240 # Pull oauth2PermissionGrants (paging)
230 −$uri = "https://graph.microsoft.com/beta/oauth2PermissionGrants?`$select=clientId,scope"
241 +# ------------------------------
242 +$uri = "https://graph.microsoft.com/beta/oauth2PermissionGrants?`$select=clientId,scope,consentType"
231243 $grants = @()
232244 while ($uri) {
233245 $resp = Invoke-MgGraphRequest -Method GET -Uri $uri
⋯ 1 unchanged line
235247 $uri = $resp.'@odata.nextLink'
236248 }
237249
238 −# Build baseline-only candidate set (paste-safe: no leading pipes)
239 −$candidates = @()
240 −foreach ($g in ($grants | Group-Object clientId)) {
241 − $spObjectId = $g.Name
250 +# ------------------------------
251 +# Build baseline-only candidate set (Jun: HashSet per clientId)
252 +# ------------------------------
253 +$scopesByClient = @{} # key: clientId (SP objectId), value: HashSet[string] (case-insensitive)
242254
243 − $scopes = $g.Group |
244 − ForEach-Object { ($_.scope -split '\s+') } |
245 − Where-Object { $_ -and $_.Trim() -ne "" } |
246 − Sort-Object -Unique
255 +foreach ($g in $grants) {
256 + $cid = $g.clientId.ToString().Trim()
257 + if (-not $cid) { continue }
247258
248 − if ($scopes.Count -gt 0) {
249 − $outside = $scopes | Where-Object { $_ -notin $BaselineScopes }
250 − if ($outside.Count -eq 0) {
251 − $candidates += [PSCustomObject]@{
252 − ServicePrincipalObjectId = $spObjectId
253 − Scopes = ($scopes -join " ")
254 − }
259 + if (-not $scopesByClient.ContainsKey($cid)) {
260 + $scopesByClient[$cid] = [System.Collections.Generic.HashSet[string]]::new(
261 + [System.StringComparer]::OrdinalIgnoreCase
262 + )
263 + }
264 +
265 + foreach ($s in ($g.scope -split '\s+')) {
266 + if ($s -and $s.Trim().Length -gt 0) {
267 + [void]$scopesByClient[$cid].Add($s.Trim())
255268 }
256269 }
257270 }
258271
272 +$candidates = foreach ($cid in $scopesByClient.Keys) {
273 + $scopes = $scopesByClient[$cid]
274 + if ($scopes.Count -le 0) { continue }
275 +
276 + $outside = $scopes | Where-Object { $_ -notin $BaselineScopes }
277 + if ($outside.Count -eq 0) {
278 + [PSCustomObject]@{
279 + ServicePrincipalObjectId = $cid
280 + Scopes = ($scopes -join ' ')
281 + }
282 + }
283 +}
284 +
285 +# ------------------------------
286 +# Pull per-app sign-in summary for last 7 days (Graph REST via Invoke-MgGraphRequest)
287 +# Endpoint: GET /beta/reports/getAzureADApplicationSignInSummary(period='D7')
288 +# In this API output, 'id' corresponds to the appId (clientId)
289 +# ------------------------------
290 +$signInSummary = @()
291 +$signInUri = "https://graph.microsoft.com/beta/reports/getAzureADApplicationSignInSummary(period='D7')"
292 +
293 +while ($signInUri) {
294 + $resp = Invoke-MgGraphRequest -Method GET -Uri $signInUri
295 +
296 + if ($resp -and $resp.value) {
297 + $signInSummary += $resp.value
298 + }
299 +
300 + # Paging (if present)
301 + $signInUri = $resp.'@odata.nextLink'
302 +}
303 +
304 +# appId -> total sign-ins (7d)
305 +$signInCountByAppId = @{}
306 +foreach ($s in $signInSummary) {
307 + $appId = $s.id
308 + if (-not $appId) { continue }
309 +
310 + # PS5.1-safe null handling
311 + $success = 0
312 + $failed = 0
313 + if ($null -ne $s.successfulSignInCount) { $success = [int]$s.successfulSignInCount }
314 + if ($null -ne $s.failedSignInCount) { $failed = [int]$s.failedSignInCount }
315 +
316 + $signInCountByAppId[$appId] = $success + $failed
317 +}
318 +
259319 $resultsTenantOwned = @()
260320 $resultsNotTenantOwned = @()
261321
262 −# Filter to tenant-owned or external apps
322 +# ------------------------------
323 +# Filter to tenant-owned or external apps; enrich with appId/displayName + sign-in counts
324 +# ------------------------------
263325 foreach ($c in $candidates) {
264326 try {
265 − $spUri = "https://graph.microsoft.com/beta/servicePrincipals/$($c.ServicePrincipalObjectId)?`$select=id,appOwnerOrganizationId"
327 + $spUri = "https://graph.microsoft.com/beta/servicePrincipals/$($c.ServicePrincipalObjectId)?`$select=id,appId,displayName,appOwnerOrganizationId"
266328 $sp = Invoke-MgGraphRequest -Method GET -Uri $spUri
267329
330 + $signinCount7d = 0
331 + if ($sp.appId -and $signInCountByAppId.ContainsKey($sp.appId)) {
332 + $signinCount7d = $signInCountByAppId[$sp.appId]
333 + }
334 +
335 + $row = [PSCustomObject]@{
336 + ServicePrincipalObjectId = $c.ServicePrincipalObjectId
337 + AppId = $sp.appId
338 + AppDisplayName = $sp.displayName
339 + AppOwnerOrganizationId = $sp.appOwnerOrganizationId
340 + Scopes = $c.Scopes
341 + UserSigninsLast7Days = $signinCount7d
342 + }
343 +
268344 if ($sp.appOwnerOrganizationId -eq $TenantId) {
269 − $resultsTenantOwned += [PSCustomObject]@{
270 − ServicePrincipalObjectId = $c.ServicePrincipalObjectId
271 − Scopes = $c.Scopes
272 − }
345 + $resultsTenantOwned += $row
273346 }
274347 else {
275 − $resultsNotTenantOwned += [PSCustomObject]@{
276 − ServicePrincipalObjectId = $c.ServicePrincipalObjectId
277 − Scopes = $c.Scopes
278 − }
348 + $resultsNotTenantOwned += $row
279349 }
280350 }
281351 catch {
282 − # Ignore non-enumerable / non-tenant-owned service principals
352 + # Ignore non-enumerable / missing service principals
283353 }
284354 }
285355
356 +# ------------------------------
286357 # Output
287 −'=== All tenant-owned apps whose delegated consent grants include ONLY the OIDC scopes + specific directory scopes ==='
288 −$resultsTenantOwned | Sort-Object ServicePrincipalObjectId
358 +# ------------------------------
359 +'=== Tenant-owned apps whose delegated consent grants include ONLY baseline scopes + user sign-ins (last 7 days) ==='
360 +$resultsTenantOwned |
361 + Sort-Object UserSigninsLast7Days -Descending |
362 + Format-Table -AutoSize
289363
290 −'=== All external apps whose delegated consent grants include ONLY the OIDC scopes + specific directory scopes ==='
291 −$resultsNotTenantOwned | Sort-Object ServicePrincipalObjectId
364 +'=== External apps whose delegated consent grants include ONLY baseline scopes + user sign-ins (last 7 days) ==='
365 +$resultsNotTenantOwned |
366 + Sort-Object UserSigninsLast7Days -Descending |
367 + Format-Table -AutoSize
292368 ```
293369
294370 #### [Usage and Insights report](#tab/usage-and-insights-report)
⋯ 120 unchanged lines

Microsoft's Markdown source from MicrosoftDocs/entra-docs, © Microsoft Corporation, under MIT. Changed lines with up to 3 unchanged lines around each; ChangeIntel kept this copy 10 Oct 04:32 UTC. The commit date is when the source changed, which can be hours or days before Learn published it.

ChangeIntel

An IT change radar: releases, security, known issues, retirements, documentation changes, and service status from public sources. Every item links to supporting evidence; dates and statuses can change after they are read.

Sources read 20 Oct 22:55 UTC · 258 of 260 readable · 256 fully read · documentation 416/439 current