Search This Blog

Jun 26, 2025

Entra ID extension attributes

 There are 4 types of extension attributes

  1. Extension attribute 1-15. This is a legacy borrow from on-prem extension attribute introduced by Exchange
  2. Directory Extension (tied to an application, but can be consumed by other applications)
  3. Schema Extension (tenant-wide)
  4. Open Extension
Please see https://learn.microsoft.com/en-us/graph/extensibility-overview

How to include "directory extension attribute" (type #2 above) in claims

  1. need to use Graph API to create claim mapping policy
  2. use below POST command and  JSON body of the Graph call

POST https://graph.microsoft.com/v1.0/policies/claimsMappingPolicies
{
  "definition": [
    "{ 
      \"ClaimsMappingPolicy\": {
        \"Version\":1,
        \"IncludeBasicClaimSet\":\"true\",
        \"ClaimsSchema\": [
          {
            \"Source\":\"user\",
            \"ID\":\"extension_hostingAppID_deviceID\",
            \"JwtClaimType\":\"deviceID\"
          }
        ]
      }
    }"
  ],
  "displayName": "IncludeDeviceID",
  "isOrganizationDefault": false
}
  1. Make a note of returned policy ID for steps followed
  2. make a POST call as below to assgin the policy to consuming app
command: POST 
https://graph.microsoft.com/v1.0//servicePrincipals/{id}/claimsMappingPolicies/$ref

            where ID is objectID of SPN 

      Body
      {
        "@odata.id": "https://graph.microsoft.com/v1.0/policies/claimsMappingPolicies/policyID"
      } // where id is policy ID
    1. Pay attention to different GUID used. In the actual policy, appID of hosting app is used(remove dashes); In POST command, objectID of consuming app is used
    2. Last step, enable app to accept custom claim
    PATCH https://graph.microsoft.com/v1.0/applications/{objID of app}
    Content-type: application/json

    {
      "api": {
        "acceptMappedClaims": true,
        "requestedAccessTokenVersion": 2
      }
    }



     

    Jun 2, 2025

    Add Google as IdP for Entra applications

     External IDP (e.g.Google) | Application (e.g. Azure Entra)

    ------------------------- | ------------------------------

    create an app in google dev console | Configure Google as IdP

    gets client ID ---> | fill in client ID

    gets client secrect ---> | fill in client secrect

    fill in redirect URIs | <--- Find URIs from MS official website

    After above, google can be added as an IdP in user flow.


    reference:

    https://learn.microsoft.com/en-us/entra/external-id/customers/how-to-google-federation-customers

    Mar 14, 2025

    Running AD cmdlets within foreach parallel script block



     Powershell's parallel foreach script block runs in its own runspace so anything defined outside of the block is not visible in it. A few steps to make AD cmdlets work:


    1. Import activedirectory module within the block. It may throw warning "Error initializing default drive", which can be safely ignored but you will have to
    2. Specify DC to establish connection via -server parameter in get-ad* cmdlets
      get-aduser -server "DC1.foobar.com" -.....
    3. The runspace won't have your credential from main session either so you have to transfer credential explicitly into script block

      $cred = get-credential
      $users | foreach -parallel {
            get-aduser -identity $_.samAccountName -credential $using:cred
      }
    4. If there are too many concurrent connections to AD, some connections may fail. Tweak to find the ThrottleLimit that works for you. 
    5. Use inputObject to return result
      $_ | add-member -notepropertyname "pn" -notepropertyValue "pv"
    6. Putting it altogether
      $cred = get-credential
      $users | foreach -parallel {
            import-module ActiveDictory
            $u=get-aduser -identity $_.samAccountName -credential $using:cred
            $_ | add-member -NotePropertyName "DN" -NotePropertyValue $u.distinguishedName
      } -ThrottleLimit 5

    [UPDATE]

    So limiting the number of threads is not ideal, with the number as long as 2, there is still chance where connection be refused by DC, not to mention we lost most of benefit if the number is too low.

    One workaround is to make sure only one runspace connects to a particular DC at a time. This can be achieved by using a file as a lock. First get list of all DCs in a domain, then when a connection is made to a DC, obtain an exclusive handle to a file that represents the DC (e.g. "dc01.lock"). Once finish access the DC, release the lock file.

    # Acquire lock before connecting to a DC
    $server = $null
    while ($null -eq $server){                   
      foreach ($DC in $using:dcs) {
        try {
              $lockFile = [system.io.file]::open("c:\temp\$($DC).lock",
                             'OpenOrCreate','ReadWrite','None')
              $server = $DC
              break
        }catch{                }
    }
    if($null -eq $server) {Start-Sleep -Milliseconds 50}
    }
    try {
        get-aduser -server $server ....

        # Release lock
        $lockFile.close()
        remove-item "c:\temp\$($server).lock" -force -erroraction silentlyContinue
       
    }catch {}



    There are other ways to implement a lock, such as described in Dave's blog, but above file lock works very well and is less complicated.