Certificate Based Authentication With Microsoft Cloud PKI – Part 3

Introduction to Multi-Tenant Certificate-Based Authentication

In our previous two blog posts, we explored the essentials of certificate-based authentication, detailing the setup of a cloud Public Key Infrastructure (PKI), deployment of Simple Key Exchange Protocol (SKEP) certificates, and implementation of certificate-based authentication with authentication strength. We also conducted thorough testing to assess the end-user experience, ensuring that our setup was both secure and user-friendly.

Building on this foundation, we now turn our attention to a more complex scenario: setting up multi-tenant certificate-based authentication. This part of our series will guide you through the necessary adjustments in Microsoft Intune, the creation of custom binding attributes in the target tenant, and the automation of these processes using Microsoft Graph API and runbooks. Our goal is to enable users from the main cloud PKI tenant to authenticate with their certificates in the target tenant, ensuring that this authentication can only occur from an Intune-managed device.

By the end of this post, you will have a comprehensive understanding of how to enforce secure, multi-tenant access using certificate-based authentication, enhancing both security and compliance across your organizational boundaries.

Business Case for Multi-Tenant Certificate-Based Authentication

Client Scenario:

One of my clients operates with a unique setup where they maintain two distinct tenants for business purposes. The primary tenant holds all the company data and services, while the secondary tenant manages all endpoint devices. This separation, while strategic, presents a significant challenge: ensuring that users access company services and data exclusively from company-owned devices that comply with corporate policies.

The Challenge:

The primary issue is enforcing access restrictions such that users can only access the company’s data and services from compliant, company-managed devices. Various solutions were considered, but many seemed impractical or overly complicated.

The Solution: Certificate-Based Authentication

During my search for an effective solution, I identified certificate-based authentication as a promising approach. Implementing this method allows me to enforce that users can only sign in using their company-issued devices. This ensures that employees cannot bypass security measures by using personal devices to access company resources.


  1. Enhanced Security: By restricting access to company services to devices with valid certificates, I can ensure that only compliant, managed devices are used.
  2. User Experience: Streamlined authentication processes for end-users, as they can securely access resources without additional hassle.
  3. Operational Efficiency: Automated management through Intune and Microsoft Graph API reduces administrative overhead.

In conclusion, multi-tenant certificate-based authentication provides a solution to my client’s access control challenges, ensuring security and compliance while maintaining a seamless user experience.


Setting up multi-tenant certificate-based authentication involves several prerequisites, including specific licensing requirements and necessary permissions. Here’s a detailed list to ensure a smooth implementation:

1. Licensing Requirements:

  • Microsoft 365 E5 or equivalent: Ensure that both the primary and secondary tenants have the appropriate Microsoft 365 licenses. The E5 license includes advanced security features necessary for certificate-based authentication and Intune management.

2. Permissions:

  • Global Administrator Role: You need to have Global Administrator permissions in both the primary and secondary tenants to configure the necessary settings and integrations.
  • Intune Administrator Role: Assign Intune Administrator permissions to manage and deploy, profiles, and certificates.

3. Technical Prerequisites:

  • Configured Cloud PKI: A fully functional cloud Public Key Infrastructure (PKI) must be in place to issue and manage certificates.
  • Intune Configuration:
    • Configuration Profiles: Deploy configuration profiles to manage certificates and other device settings.
    • SCEP Certificates: Deploy Simple Certificate Enrollment Protocol (SCEP) certificates to devices for authentication.
  • Microsoft Graph API Access: Enable and configure Microsoft Graph API to automate tasks, such as creating custom binding attributes and managing user/device information.


The following design outlines the architecture and flow for implementing multi-tenant certificate-based authentication. The diagram illustrates how users in Tenant A can securely access resources in Tenant B using certificate-based authentication, managed by Intune and automated through Azure services.

Key Components

Tenant A:

  • Azure AD: Manages user identities and provides authentication.
  • Managed Identity: Facilitates secure communication and automation without requiring credentials.
  • Azure Subscription: Hosts the necessary Azure resources for automation and management.
  • Automation Account: Executes runbooks to automate the process of creating custom binding attributes and managing user information.

Tenant B:

  • Azure AD: Manages user identities and resources specific to Tenant B.
  • App Registration: Registers applications required for the authentication process and integration between tenants.
  • User 1 (Tenant B): Represents the end-user accessing resources in Tenant B.

Intune Endpoint

  • Device Management: Ensures that only compliant, company-managed devices can access resources. Deploys and manages SCEP certificates on user devices.

User Certificate:

  • Authentication: The certificate issued to user devices, ensuring that only authenticated and compliant devices can access Tenant B’s resources.


Automation and Integration (Blue Lines):

  • The Automation Account in Tenant A signs in with a Managed Identity to match the users in both tenants.
  • Authentication in Tenant B is facilitated by an App Registration.
  • Once the accounts in both tenants are matched, the Automation Account adds custom authorization information to the user’s account in Tenant B. This customization allows the certificate deployed to the user’s endpoint in Tenant A to be identified and used for authentication in Tenant B.

User Linking and Access Control (Green Lines):

  • User devices in Tenant A are managed by Intune, which deploys SCEP certificates to these devices.
  • When User 1 in Tenant A attempts to access resources in Tenant B, the certificate on the device is used for authentication.
  • The setup ensures that only devices with valid certificates issued by Tenant A can authenticate and access resources in Tenant B.


Now that we’ve covered the basics of the design, let’s move on to the setup process. Initially, I thought this setup would involve only a few steps, but as you can see below, it quickly expanded. To be thorough, I’ve also included steps for creating a storage account to upload basic logs. So, if you’re ready to follow along and set this up, now is the time to grab a coffee and get started!

Tenant B

Security Group

We will be using this security Group to scope our demo
Creating a security group in EntraID (Azure Active Directory) is a straightforward process. Follow these steps to get it done:

  • Log into the Azure Portal: Azure Portal
  • Go to Azure Active Directory
  • Navigate to “Groups”
  • Click “+ New group”
  • Select “Security” as Group type
  • Name your group and optionally add a description
  • Choose “Assigned” as Membership type
  • Add your test user as a member
  • Click “Create” to finish

This security group will now be used to assign all the policies we create, ensuring they are scoped to just the one test user.

You should also create a security group in tenant A

Certificate authority

Setup your certificate authority the same way we set it up in part 1 of this series but now we will do in tenant B.
Click here to see how we did it in part 1

Certificate Based Authentication

for more details go to part 1

For the Demo:

  • Protection Level: Multi-Factor Authentication (MFA)
  • Affinity: Low
  • Username Binding:
    • Certificate field PrincipalName
    • Affinity binding Low
    • User attribute CertificateUserIDs

Authentication Strength

Create a Certificate based authentication strength go to part 2 on how to do it.

Conditional Acces Policy

Create a CA Policy with the just created authentication Strength and scope it on the test group and exclude the test group on all other CA policy’s for testing purposes. for more detail go to part 2

Enterprise Application

Navigate to Azure Active Directory:

  • In the left-hand menu, select “Azure Active Directory”.

Create a New App Registration:

  • Click on “App registrations”.
  • Click on “+ New registration”.
  • Fill in the registration details:
    • Name: CERT_AUTH_DEMO
    • Supported account types: Choose the appropriate option (e.g., “Accounts in this organizational directory only”).
    • Redirect URI: Leave it blank if not applicable.
  • Click “Register”.

Configure API Permissions:

  • Go to “API permissions” in the left-hand menu.
  • Click “+ Add a permission”.
  • Select “Microsoft Graph”.

Add Group Permissions:

  • Choose “Application permissions”.
  • Add:
    • Group.Read.All: Read all groups.
    • GroupMember.Read.All: Read all group memberships.
    • User.ReadWrite.All: Read and write all users’ full profiles.

Grant Admin Consent:

  • In the “API permissions” pane, click “Grant admin consent for [Your Organization]” for each permission.
  • Confirm that all permissions are granted for CERT_AUTH_DEMO.

Azure Automation

EntraID Managed Identity

Create Identity

A User-Assigned Managed Identity (UAMI) in Azure provides a secure and convenient way to authenticate your applications and services without the need to manage credentials manually. When using a User-Assigned Managed Identity to authenticate with Microsoft Graph API, you can securely access Azure resources and services. Here’s a step-by-step guide on how to set this up:

Navigate to Resource Groups:

  • In the left-hand menu, select “Resource groups”.
  • Click on the Resource Group you created in the previous steps.

Add a Managed Identity:

  • In the Resource Group’s menu, select “Add” to create a new resource.
  • In the “Search the Marketplace” box, type “Managed Identity” and select “User Assigned Managed Identity” from the list.
  • Click “Create”.

Configure the Managed Identity:

  • Subscription: Ensure the correct subscription is selected.
  • Resource Group: Ensure the correct Resource Group is selected.
  • Name: Enter a unique name for the Managed Identity.
  • Region: Select the same region as the Resource Group.

Review and Create:

  • Click “Review + Create”.
  • Review the details and click “Create” to finalize the creation.
Assign Graph API Permissions

Managed Identity Permissions Script

The script provided below sets up the necessary permissions for a managed identity to interact with Microsoft Graph, which is essential for managing Intune configurations

You might wonder why we are using a script instead of the UI to assign Graph permissions. The reason is simple: currently, the UI does not support assigning Graph permissions to a managed identity. This limitation necessitates the use of a script to automate the process and ensure the managed identity has the required permissions to perform its tasks.

Full script on Github

Script Breakdown

Module Installation and Importation:

his part ensures that the AzureAD module is installed and imported. This module is crucial for interacting with Azure Active Directory (AAD) using PowerShell.

# Install the Microsoft Graph PowerShell module if not already installed.
# Install-Module Microsoft.Graph -force -Scope CurrentUser
Import-Module Microsoft.Graph

Setting Up Principal ID

You need to provide your Azure AD tenant ID and the managed identity’s principal ID. These values can be found in the Azure portal under the Azure Active Directory section.

# Define your pricipalId
$principalId = "74d5733a-aa9b-4171-aff9-972412964dd5"

Defining Necessary Permissions:

Here, we define an array of permissions that the managed identity will need. These permissions are specific to operations related to Intune and Microsoft Graph.

# Check the Microsoft Graph documentation for the permission you need for the operation.
$permissions = @(

Connect MgGraph

# Connect to Microsoft Graph.
Connect-MgGraph -Scopes "Application.ReadWrite.All", "Directory.ReadWrite.All", "RoleManagement.ReadWrite.Directory, AppRoleAssignment.ReadWrite.All"

Retrieving the Microsoft Graph Service Principal

This command retrieves the service principal for Microsoft Graph. The AppId 00000003-0000-0000-c000-000000000000 is a unique identifier for Microsoft Graph in Azure AD.

# Get the service principal for Microsoft Graph.
$GraphServicePrincipal = Get-MgServicePrincipal -Filter "appId eq '00000003-0000-0000-c000-000000000000'"

Assigning Permissions to the Managed Identity

This loop iterates over each permission defined earlier, finds the corresponding app role in the Microsoft Graph service principal, and assigns it to the managed identity. This step ensures that the managed identity has the necessary permissions to perform operations on Microsoft Graph, which in turn allows for automated management of Intune policies.

# Assign permissions to the managed identity service principal.
foreach ($p in $permissions) {
    $AppRole = $GraphServicePrincipal.AppRoles | Where-Object { $_.Value -eq $p -and $_.AllowedMemberTypes -contains "Application" }
    if ($AppRole) {
        New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $principalId -PrincipalId $principalId -ResourceId $GraphServicePrincipal.Id -AppRoleId $AppRole.Id
    } else {
        Write-Host "Permission $p not found."

# Disconnect from Microsoft Graph.
Check permissions

Navigate to EntraID

  1. In the Azure Portal, use the search bar at the top to search for “EntraID”.
  2. Click on “Azure Active Directory” from the search results.

App Registrations

  1. In the Azure Active Directory pane, select “App registrations” from the left-hand menu.
  2. Find the application (or managed identity) for which you want to check or manage Graph API permissions. If the managed identity isn’t listed, ensure you have created an app registration for it.

API Permissions

  1. Click on the application to open its settings.
  2. In the application settings pane, select “API permissions” from the left-hand menu.
  3. Here, you will see a list of all the API permissions granted to the application, including Microsoft Graph API permissions.

Storage Account

Navigate to the Storage Accounts Section

  • In the Azure Portal, use the search bar at the top and type “Storage Accounts”.
  • Click on “Storage Accounts” from the search results.

Create a New Storage Account

  • Click the “+ Create” button.
  • Select your subscription and resource group.
  • Enter a unique name for your storage account.
  • Leave all other settings as default.

Review and Create

  • Click on the “Review + create” button.
  • Review your settings and click “Create” to deploy the storage account.

Creating a Container in the Storage Account

Access the Storage Account

  • Once the storage account is created, navigate to it from the list of storage accounts.
  • Click on the storage account to open its details.

Navigate to Containers

  • In the left-hand pane, under “Data storage”, click on “Containers”.

Create a New Container

  • Click the “+ Container” button.
  • Enter a name for the container.
  • Click “Create” to create the container.

Access the Access Control (IAM) Section

  • In the left-hand pane, click on “Access control (IAM)”.

Add a Role Assignment

  • Click the “+ Add” button and then select “Add role assignment”.
  • In the “Role” dropdown, search for and select “Storage Blob Data Contributor”.

Select the Managed Identity

  • Under “Assign access to”, select “Managed identity”.
  • Click on “Select members” and search for the managed identity you want to assign the role to.
  • Select the managed identity and click “Select”.

Review and Assign the Role

  • Review the settings and ensure the correct role and managed identity are selected.
  • Click “Review + assign” to finalize the assignment.

Verify the Role Assignment

  • Return to the “Access control (IAM)” section.
  • Check the “Role assignments” tab to verify that the managed identity has been assigned the “Storage Blob Data Contributor” role.

Azure Automation Account

Creating an Azure Automation Account using the Azure Portal is simple. Follow these steps:

Navigate to Create a Resource:

  • In the left-hand menu, click on “Create a resource”.
  • In the “Search the Marketplace” box, type “Automation” and select “Automation” from the list.
  • Click “Create” to start the creation process.

Configure the Automation Account:

  • Subscription: Select the appropriate subscription where you want to create the Automation Account.
  • Resource Group: Select the Resource Group you created earlier.
  • Name: Enter a unique name for the Automation Account.
  • Region: Select the same region as your Resource Group.
  • Create Azure Run As Account: Choose whether to create a Run As account, which is used to authenticate with Azure services. For most scenarios, you should select “Yes”.

Review and Create:

  • Click “Review + Create”.
  • Review the details and click “Create” to finalize the creation.
Connect Managed Identity to automation Account

Follow these steps to connect the Managed Identity you created to your Azure Automation Account:

Navigate to the Automation Account:

  • In the left-hand menu, select “Resource groups” and then click on the Resource Group where your Automation Account is located.
  • Click on the Automation Account you created earlier.

Enable User-Assigned Managed Identity:

  • In the Automation Account’s menu, select “Identity” under the “Account Settings” section.
  • On the “User assigned” tab, Click “+ Add”.
  • Select the managed identity you created and click “add”
Powershell Az.storage Module

Navigate to Your Automation Account

  • In the Azure Portal, use the search bar at the top and type “Automation Accounts”.
  • Select your Automation Account from the list.

Open the Modules Gallery

  • In the left-hand pane, under “Shared Resources”, click on “Modules Gallery”.

Search for the Az.Storage Module

  • In the Modules Gallery, use the search bar to search for Az.Storage.
  • Locate Az.Storage in the search results.

Import the Az.Storage Module

  • Click on Az.Storage from the search results.
  • In the Az.Storage module details page, click the “Import” button.
  • A new window will open. Select the appropriate options if prompted and then click “OK” to import the module.

Verify the Module is Imported

  • Go back to the “Modules” section under “Shared Resources” in your Automation Account.
  • Ensure Az.Storage is listed among the imported modules.

PowerShell Runbook

Navigate to Your Automation Account

  • In the Azure Portal, use the search bar at the top and type “Automation Accounts”.
  • Select your Automation Account from the list.

Access the Runbooks Section

  • In your Automation Account, navigate to “Runbooks” under “Process Automation”.

Create a New Runbook

  • Click the “+ Create a runbook” button.

Select Runbook Type and Runtime Version

  • Enter the necessary details such as the runbook name and description.
  • For the Runbook type, select “PowerShell”.
  • For the runtime version, select “7.2”.

Write Your PowerShell Script

  • In the runbook editor, copy and paste the script we cover below.

Save and Publish the Runbook

  • After writing your script, click “Save” to save your changes.
  • Click “Publish” to make the runbook available for use.

Script Breakdown

The PowerShell script is designed to manage and synchronize user authorization information between two Azure Active Directory (Azure AD) tenants. The core function of this script is to retrieve user membership information from groups in two different Azure AD tenants (Tenant A using Managed Identity and Tenant B using App Registration), compare the memberships, update the user authorization information in Tenant B based on Tenant A’s data, and log the matching users and non matching users to a log file and upload it to a blob storage.

for the latest version of my script please download it from Github.


Declare your variable for demo purposes the secretes are plain text but if you implement this script please DONT do this 😉 use an Azure Key Vault

# Tenant A details (Managed Identity)
$TenantAId = "<TenantAId>"
$GroupIdA = "<GroupIdA>"
$miAppId = "<ManagedIdentityAppId>"

# Tenant B details (App Registration)
$TenantBId = "<TenantBId>"
$TenantBClientId = "<TenantBClientId>"
$TenantBClientSecret = "<TenantBClientSecret>"
$GroupIdB = "<GroupIdB>"

# Azure Blob Storage details
$StorageAccountName = "<StorageAccountName>"
$ContainerName = "cert-auth"
$BlobEndpoint = "https://$StorageAccountName.blob.core.windows.net/"

Log File Initialization

The script starts by initializing a log file to keep a record of all activities and errors encountered during execution.

# Log file path
$LogFilePath = ".\Cert_Auth.log"

# Initialize log file
if (Test-Path $LogFilePath) {
    Remove-Item $LogFilePath
New-Item -Path $LogFilePath -ItemType File

Access Token Generation

Access tokens are essential for authenticating and authorizing requests to Microsoft Graph API. The script retrieves OAuth2 access tokens for both tenants:

  • Tenant A uses Managed Identity.
  • Tenant B uses client credentials from App Registration.
# Function to get an access token for Microsoft Graph API using managed identity
function Get-GraphAPIAccessTokenPost {
    param (
    $url = $env:IDENTITY_ENDPOINT
    $headers = @{
        'Metadata' = 'True'
    $body = @{
        'resource' = 'https://graph.microsoft.com'
        'client_id' = $miAppId
    # Send POST request to get access token
    $accessToken = Invoke-RestMethod $url -Method 'POST' -Headers $headers -ContentType 'application/x-www-form-urlencoded' -Body $body
    return $accessToken.access_token

# Function to get access token for App Registration
function Get-AccessToken-AppRegistration {
    param (
    $body = @{
        grant_type    = "client_credentials"
        scope         = "https://graph.microsoft.com/.default"
        client_id     = $ClientId
        client_secret = $ClientSecret

    $response = Invoke-RestMethod -Method Post -Uri "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token" -ContentType "application/x-www-form-urlencoded" -Body $body
    return $response.access_token

# Get access tokens for both tenants
$AccessTokenA = Get-GraphAPIAccessTokenPost -miAppId $miAppId
$AccessTokenB = Get-AccessToken-AppRegistration -TenantId $TenantBId -ClientId $TenantBClientId -ClientSecret $TenantBClientSecret

Group Members Retrieval

The script fetches members of specific Azure AD groups from both tenants.

# Function to get group members with paging
function Get-GroupMembers {
    param (
    $headers = @{
        "Authorization" = "Bearer $AccessToken"
    $members = @()
    $url = "https://graph.microsoft.com/beta/groups/$GroupId/members?$expand=*"

    try {
        do {
            $response = Invoke-RestMethod -Method Get -Uri $url -Headers $headers
            $members += $response.value
            $url = $response.'@odata.nextLink'
        } while ($url -ne $null)
        return $members
    } catch {
        Log-Message "Failed to get members for Group ID $GroupId. Error: $_"
        return $null

# Get group members from both tenants
$MembersA = Get-GroupMembers -AccessToken $AccessTokenA -GroupId $GroupIdA
$MembersB = Get-GroupMembers -AccessToken $AccessTokenB -GroupId $GroupIdB

User Comparison and Logging

It compares user memberships between the two tenants and logs the results:

  • Users in both Tenant A and Tenant B.
  • Users only in Tenant A.
  • Users only in Tenant B.
if ($MembersA -and $MembersB) {
    # Convert to arrays of usernames (stripping domain) and user IDs for comparison and updates
    $UsersA = @{}
    $MembersA | ForEach-Object { $UsersA[$_.userPrincipalName.Split('@')[0]] = $_.id }

    $UsersB = @{}
    $MembersB | ForEach-Object { $UsersB[$_.userPrincipalName.Split('@')[0]] = $_.id }

    # Find matches, only in A, and only in B
    $Matches = $UsersA.Keys | Where-Object { $UsersB.Keys -contains $_ }
    $OnlyInA = $UsersA.Keys | Where-Object { $UsersB.Keys -notcontains $_ }
    $OnlyInB = $UsersB.Keys | Where-Object { $UsersA.Keys -notcontains $_ }

    # Log results
    Log-Message "Matched Users:"
    $Matches | ForEach-Object { Log-Message $_ }

    Log-Message "`nUsers only in Tenant A:"
    $OnlyInA | ForEach-Object { Log-Message $_ }

    Log-Message "`nUsers only in Tenant B:"
    $OnlyInB | ForEach-Object { Log-Message $_ }
} else {
    Log-Message "Failed to retrieve group members from one or both tenants."

Updating User Authorization in Tenant B

For matched users, the script updates their authorization information in Tenant B.

# Function to update user authorization info in Tenant B
function Update-UserAuthorizationInfo {
    param (
    $headers = @{
        "Authorization" = "Bearer $AccessToken"
        "Content-Type"  = "application/json"

    $body = @{
        "authorizationInfo" = @{
            "certificateUserIds" = @("X509:<PN>CN=$UserPrincipalName")
    } | ConvertTo-Json

    $url = "https://graph.microsoft.com/v1.0/users/$UserId"
    try {
        Invoke-RestMethod -Method Patch -Uri $url -Headers $headers -Body $body
        Log-Message "Successfully updated authorization info for user $UserPrincipalName"
    } catch {
        Log-Message "Failed to update authorization info for user $UserPrincipalName. Error: $_"

# Update authorization info in Tenant B for matched users
foreach ($username in $Matches) {
    $UserIdB = $UsersB[$username]
    Update-UserAuthorizationInfo -AccessToken $AccessTokenB -UserId $UserIdB -UserPrincipalName $username

Upload Log File to Azure Blob Storage

Finally, the script uploads the log file to Azure Blob Storage, ensuring a structured path for easy retrieval and monitoring.

# Function to upload log file to Azure Blob Storage
function Upload-LogFileToBlob {
    param (
        [string]$ManagedIdentityClientId  # Add this parameter if using user-assigned managed identity
    $currentDate = Get-Date -Format "yyyy/MM/dd"
    $blobPath = "cert-auth/$currentDate/log_$(Get-Date -Format 'yyyyMMddHHmmss').log"
    try {
        az login --identity --username $ManagedIdentityClientId --allow-no-subscriptions
        az storage blob upload --account-name $StorageAccountName --container-name $ContainerName --name $blobPath --file $LogFilePath --auth-mode login --output none
        Log-Message "Successfully uploaded log file to $BlobEndpoint$ContainerName/$blobPath"
        Write-Output "Successfully uploaded log file to $BlobEndpoint$ContainerName/$blobPath"
    } catch {
        Log-Message "Failed to upload log file to Azure Blob Storage. Error: $_"
        Write-Output "Failed to upload log file to Azure Blob Storage. Error: $_"

# Upload log file to Azure Blob Storage
Upload-LogFileToBlob -LogFilePath $LogFilePath -TenantId $TenantAId -StorageAccountName $StorageAccountName -ContainerName $ContainerName -SASToken $SASToken

Log File Example


This PowerShell script demonstrates an effective approach to managing and synchronizing user authorization information between two Azure Active Directory (Azure AD) tenants, Tenant A (using Managed Identity) and Tenant B (using App Registration). The script performs several critical functions to ensure user authorization information is consistent across both tenants and provides comprehensive logging for auditing purposes. The key actions include:

  • Log File Initialization: The script initializes a log file to record activities and errors, ensuring a new log file is created each time the script runs.
  • Access Token Generation: The script retrieves OAuth2 access tokens for Tenant A using Managed Identity and for Tenant B using client credentials, enabling secure API calls.
  • Group Members Retrieval: The script retrieves members of specific Azure AD groups from both tenants, essential for comparing user memberships.
  • User Comparison and Logging: It compares group memberships between the two tenants, logs matched users, users only in Tenant A, and users only in Tenant B.
  • Updating User Authorization in Tenant B: The script updates the authorization information for matched users in Tenant B, ensuring synchronization.
  • Uploading the Log File: After execution, the script uploads the log file to Azure Blob Storage, maintaining a structured path for easy access and audit trails.

By following this process, the script ensures that user authorization information is kept up-to-date and synchronized across both Azure AD tenants. Additionally, it provides a reliable audit trail by uploading the logs to a designated storage account after each run, which is crucial for maintaining security and compliance. This script serves as a starting point for managing user synchronization between Azure AD tenants and can be customized further to meet specific production needs.


In Microsoft Intune, the Common Name (CN) attribute is a key part of Simple Certificate Enrollment Protocol (SCEP) certificate requests. When configuring SCEP certificates, using the CN attribute with the username makes it easy to identify the user. Here’s a straightforward explanation of how this works and why it’s beneficial:

Common Name (CN) Attribute

  • What It Is: The Common Name is an important part of a certificate that specifies the name of the certificate holder. In our case, it’s the user’s username.
  • Example: CN=harry.potter

Why Use CN with Username?

  • Unique Identification: Using the username in the CN attribute makes sure each certificate is tied to a specific user. This helps in easily identifying who the certificate belongs to.
  • Simple and Clear: Usernames are easy to manage and recognize, making the process simpler.
  • Widely Compatible: Many systems and applications use the CN attribute for identification, so it’s a versatile choice.
  • Easy Matching: When syncing user information between two Azure AD tenants, using the username as the CN makes it easier to match users if the usernames are the same in both tenants.

Benefits of Using KSP and TPM

  • Enhanced Security:
    • KSP (Key Storage Provider): Manages keys securely, using modern cryptographic standards.
    • TPM (Trusted Platform Module): Provides hardware-based security for cryptographic operations, making keys resistant to extraction and unauthorized use.
  • Non-Exportable Certificates: With TPM KSP, private keys are non-exportable by design. This means the keys can’t be transferred or copied from the device, protecting against key theft.

Updating the SCEP Certificate Profile

To use the CN attribute with the username in your existing SCEP certificate profile:

  1. Go to Intune:
  2. Edit the Existing Profile:
    • Find and select the SCEP certificate profile you created.
    • Click on “Properties” to edit it.
  3. Update the Subject Name Format:
    • In the “Certificate properties” section, set the “Subject name format” to CN={{Username}}.
    • This ensures the Common Name in the certificate will be the user’s username.
  4. Save Your Changes:
    • Click “Review + save” to apply the changes.


If the user is harry.potter, with the updated configuration CN={{Username}}, the certificate will show:


How This Fits with the PowerShell Script

Using the username as the CN attribute in SCEP certificates helps synchronize user information between two Azure AD tenants. The script above compares and updates user info based on group memberships in both tenants. Matching users is much easier if the usernames are consistent.

Key Benefits in This Context

  • Easy User Matching: The script compares group members between Tenant A and Tenant B. Using the username as the CN makes it easy to match users across both tenants.
  • Clear Identification: The CN attribute with the username provides clear identification in logs and updates, making the process transparent.
  • Consistent Attribute Use: Using the username consistently across both tenants reduces complexity and errors.

Risks of Using Low-Affinity Binding for Authentication

While using the username as the CN attribute simplifies user matching, there are risks:

  • Lower Security: Usernames alone are not very secure since they can be guessed or duplicated.
  • Weak Authentication: Stronger methods like multi-factor authentication (MFA) or unique digital certificates are better for security.
  • Attack Vulnerability: Systems using low-affinity bindings are more prone to phishing, brute force attacks, and social engineering.

Exportability and Security of SCEP Certificates

  • Non-Exportable: With KSP and TPM, private keys are non-exportable. This means they can’t be moved to another device.


Using the CN attribute with the username in SCEP certificates makes user matching and synchronization between two Azure AD tenants straightforward. This approach, combined with the security of KSP and TPM, ensures that certificates are non-exportable and well-protected. This enhances both security and efficiency, providing a reliable solution for managing user identities across Azure AD environments.

Note if you have already deployed this to the testing user, please revoke the previous certificate to ensure they receive a new certificate with the added information.

User Experience

As shown in the screenshots below, the authentication process for the user is identical to what we documented in Part 2. However, instead of logging in with their Tenant A UPN, the user logs in with their Tenant B UPN and can sign in seamlessly using the certificate from Tenant A.


In this series, we’ve already covered the following. Starting with the basics in Part 1, we set up Cloud PKI and deployed SCEP certificates. In Part 2, we configured authentication strengths and conditional access policies. Now, in this part, we’ve extended these concepts to multi-tenant environments.

In this section, we created necessary Azure resources and linked two tenants using an automation account. This setup allows authentication information to be added so that a certificate from Tenant A can be used to authenticate in Tenant B.

This isn’t the end of our journey. In the next part, we’ll explore Autopilot deployments and how they integrate with certificate-based authentication.

Leave a Reply

Your email address will not be published. Required fields are marked *