Practical Tip #02 - automatically update Conditional Access rules for admin roles

Lesedauer 5 Minuten

Problem: Conditional access rules are applied specifically to roles that include the word “Administrator” in their names. Microsoft is constantly adding new roles, but these are not automatically included in such rules.

Task: Automate the addition of new roles using scripts.

Script structure

You can find the complete script in my Script Nest. The following sections explain how it works in detail.

Choose options

At the beginning of the script, two variables need to be modified. These determine whether the script runs in test mode or in production mode (i.e., with actual changes). It is recommended that you run the script at least once in test mode to verify its behavior in your own environment.

In addition, the script can be run either interactively (i.e., with a user login) or automatically (using Azure Automation).

The variables are created with the $Script: scope so that they can be accessed within all functions.

# Choose simulation mode: Set to $true to only simulate changes. Set to $false to apply changes live.
$Script:WhatIf = $true

# Choose authentication mode: Set to 'Interactive' for user prompt or 'Automated' for service principal authentication.
$Script:ExecutionMode = 'Interactive'
PowerShell

Checking and installing the necessary modules

The CheckModules function verifies whether the PowerShell modules required for execution (Microsoft.Graph.Authentication, Microsoft.Graph.Identity.SignIns) are installed. This is only necessary if the script is run interactively. In Azure Automation, a suitable runtime environment must be created for the script that includes the necessary modules.

function CheckModules
   {
   # Check if needed modules are installed and install, if not
   if (!(Get-InstalledModule -Name Microsoft.Graph.Authentication))
      {
      Write-Host 'Module Microsoft.Graph.Authentication is not installed. Installing for current user...' -ForegroundColor Yellow
      Install-Module -Name Microsoft.Graph.Authentication -Scope CurrentUser -Force
      }
   if (!(Get-InstalledModule -Name Microsoft.Graph.Identity.SignIns))
      {
      Write-Host 'Module Microsoft.Graph.Identity.SignIns is not installed. Installing for current user...' -ForegroundColor Yellow
      Install-Module -Name Microsoft.Graph.Identity.SignIns -Scope CurrentUser -Force
      }
   }
PowerShell

Reviewing and updating Conditional Access rules

The CheckAndUpdateCARules function first identifies all Entra roles that contain the word “Administrator” in their names.

It then identifies all rules that apply specifically to administrator roles and checks whether the assigned roles match the identified roles. If not, the rules are updated to include the missing administrator roles. If test mode is enabled, the script simply outputs which policy would be updated.

If no roles are missing, the script outputs a message to that effect and terminates.

function CheckAndUpdateCARules
   {
   if ($WhatIf) 
      {
      Write-Output  '==================================================' -ForegroundColor Yellow
      Write-Output ' RUNNING IN WHAT-IF MODE (SIMULATION ON)          ' -ForegroundColor Yellow
      Write-Output  ' No live changes will be made to your policies.   ' -ForegroundColor Yellow
      Write-Output  '==================================================' -ForegroundColor Yellow
      }

   # Connect to Microsoft Graph
   Write-Output 'Connecting to Microsoft Graph...' -ForegroundColor Yellow
   if ($AuthenticationMode -eq 'Interactive'){Connect-MgGraph -Scopes 'Policy.ReadWrite.ConditionalAccess', 'RoleManagement.Read.Directory'}
   if ($AuthenticationMode -eq 'Automated'){Connect-MgGraph -Identity}

   # Retrieve all directory roles containing "Administrator" in their name
   Write-Output 'Retrieving all directory roles with "Administrator" in the name...' -ForegroundColor Cyan
   $allAdminRoles = Get-MgDirectoryRoleTemplate | Where-Object { $_.DisplayName -like "*Administrator*" }

   if ($null -eq $allAdminRoles -or $allAdminRoles.Count -eq 0) 
      {
      Write-Error "No directory roles found containing the word 'Administrator' in the name."
      return
      }

   Write-Output "Total administrator roles found: $($allAdminRoles.Count)" -ForegroundColor Green

   # Retrieve all Conditional Access policies
   Write-Output "Retrieving all Conditional Access policies..." -ForegroundColor Cyan
   $caPolicies = Get-MgIdentityConditionalAccessPolicy

   ForEach ($policy in $caPolicies) 
      {
      # Check if the policy targets specific roles
      $includedRoles = $policy.Conditions.Users.IncludeRoles
        
      if ($null -ne $includedRoles -and $includedRoles.Count -gt 0) 
         {
         # Check if at least one of the included roles is an administrator role
         $hasAdminRole = $false
         foreach ($roleId in $includedRoles) 
            {
            if ($roleId -in $allAdminRoles.Id) 
               {
               $hasAdminRole = $true
               break
               }
            }
            
         # If the policy applies to at least one administrator role
         if ($hasAdminRole) 
            {
            Write-Output '--------------------------------------------------' -ForegroundColor Yellow
            Write-Output "Policy matched: $($policy.DisplayName)" -ForegroundColor Yellow
                
            # Identify missing administrator roles
            $missingRoles = @()
            foreach ($adminRole in $allAdminRoles) 
               {
               if ($adminRole.Id -notin $includedRoles) 
                  {
                  $missingRoles += $adminRole.Id
                  }
               }
                
            if ($missingRoles.Count -gt 0) 
               {
               Write-Output "The following roles are missing in this policy: $($missingRoles.Count)" -ForegroundColor Magenta
                    
               # Create a new list of roles to include (Existing + Missing)
               $updatedRoles = $includedRoles + $missingRoles
                    
               # Prepare the update object
               $updateParams = @{
                  Conditions = @{
                     Users = @{
                        IncludeRoles = $updatedRoles
                        }
                  }
               }
                    
               # Update or simulate the policy update
               if ($WhatIf) {Write-Output "[WHAT-IF] Would update policy '$($policy.DisplayName)' to include all administrator roles." -ForegroundColor DarkYellow}
               else {
                  try {
                      Update-MgIdentityConditionalAccessPolicy -ConditionalAccessPolicyId $policy.Id -BodyParameter $updateParams
                      Write-Output "Policy '$($policy.DisplayName)' successfully updated." -ForegroundColor Green
                      }
                  catch {
                        Write-Error "Failed to update policy '$($policy.DisplayName)': $_"
                        }
                    }
               }
            else {
                 Write-Output "Policy '$($policy.DisplayName)' already contains all administrator roles." -ForegroundColor Green
                 }
            }
        }
    }

    Write-Output '--------------------------------------------------' -ForegroundColor Cyan
    Write-Output  'Review and update process completed.' -ForegroundColor Cyan
   }
PowerShell

Execute functions

Functions, as such, are merely a logical container for executing commands. However, they must be explicitly executed for an action to actually take place. Therefore, the functions are referenced at the end of the script and executed as a result.

The CheckModules function is executed only when the script is run interactively, since Azure Automation assumes that a suitable runtime environment has been configured.

# Execute functions
if ($ExecutionMode -eq 'Interactive'){CheckModules}
CheckAndUpdateCARules
PowerShell


Liked this article? Share it!