Remove-ElevatedAccess-Automation.ps1

<#PSScriptInfo
 
.VERSION 1.0.0
 
.GUID bea7afb9-c39a-42b0-9e88-76fc7c026b9a
 
.AUTHOR Sebastian Flæng Markdanner
 
.COMPANYNAME Cloudy With a Chance of Security
 
.COPYRIGHT (c) 2025 Cloudy With a Chance of Security. All rights reserved.
 
.TAGS Azure, Automation, Entra, Elevated Access, Managed Identity
 
.LICENSEURI https://opensource.org/licenses/MIT
 
.PROJECTURI https://chanceofsecurity.com/
 
.EXTERNALMODULEDEPENDENCIES Az.Accounts
 
.RELEASENOTES Initial release of the script to remove Microsoft Entra Elevated Access using Azure Automation Account with Managed Identity.
 
.PRIVATEDATA
 
#>


<#
 
.SYNOPSIS
    Removes Microsoft Entra Elevated Access (User Access Administrator role) from a user using Azure Automation Account with Managed Identity.
 
.DESCRIPTION
    This script is designed to run in an Azure Automation account and be triggered by a Logic App.
    It uses the System Assigned Managed Identity of the Automation Account to authenticate to Azure.
    The script removes Elevated Access in Entra by removing the User Access Administrator
    role assignment at the root scope ("/") for the specified user.
     
    The script provides structured output that can be consumed by the calling Logic App.
 
.PARAMETER UserId
    Required. The Object ID of the user whose elevated access should be removed.
    The script automatically converts User Principal Names (email addresses) to Object IDs.
 
.NOTES
    Author: Sebastian Flæng Markdanner (Modified for Azure Automation)
    Website: https://chanceofsecurity.com
    Version: 2.0
    Last Updated: 2025-02-18
    License: MIT
    LicenseUri: https://opensource.org/licenses/MIT
    ProjectUri: https://chanceofsecurity.com
     
    Requirements:
    - Azure Automation Account with System Assigned Managed Identity
    - The Managed Identity must have appropriate permissions (Global Administrator or User Access Administrator) @ root
    - Az modules imported in the Automation Account
 
#>
 

[CmdletBinding()]
param(
    [Parameter(Mandatory=$true)]
    [string]$UserId
)

# Add timestamp function
function Get-Timestamp {
    return "[{0:yyyy-MM-dd HH:mm:ss}]" -f (Get-Date)
}

# Initialize output object for Logic App
$outputObject = [PSCustomObject]@{
    Timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
    Action = "Remove Elevated Access"
    UserId = $UserId
    Status = "Started"
    Message = "Initializing removal process"
    RoleAssignmentId = ""
    Success = $false
}

try {
    # Connect using the Automation Account's Managed Identity
    Write-Output "Connecting to Azure using Managed Identity..."
    Connect-AzAccount -Identity

    # Define API URLs and versions
    $managementApi = "https://management.azure.com"
    $apiVersion = "2022-04-01"
    $roleDefinitionApi = "$managementApi/providers/Microsoft.Authorization/roleDefinitions?api-version=$apiVersion&`$filter=roleName+eq+'User Access Administrator'"

    # Get access token for Azure Management API
    $accessTokenARM = (Get-AzAccessToken -ResourceUrl $managementApi).Token

    # Set request headers for Azure ARM API calls
    $headersARM = @{
        "Authorization" = "Bearer $accessTokenARM"
        "Content-Type"  = "application/json"
    }

    # Step 1: Get User Access Administrator Role Definition ID
    Write-Output "Retrieving User Access Administrator role definition..."
    $roleResponse = Invoke-RestMethod -Uri $roleDefinitionApi -Method Get -Headers $headersARM
    $roleDefinitionId = $roleResponse.value[0].id

    if (-not $roleDefinitionId) {
        throw "Failed to retrieve User Access Administrator Role Definition ID. Please ensure the Managed Identity has sufficient permissions."
    }

    Write-Output "Retrieved User Access Administrator Role ID: $roleDefinitionId"

    # Step 2: Convert UPN to Object ID if the provided UserId is an email/UPN
    if ($UserId -notmatch '^[{]?[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}[}]?$') {
        Write-Output "Converting user principal name to Object ID..."
        try {
            $objId = (Get-AzADUser -UserPrincipalName $UserId).Id
            if (-not $objId) {
                throw "Unable to find Object ID for user $UserId"
            }
            $UserId = $objId
        }
        catch {
            throw "Failed to retrieve Object ID for user $UserId. Error: $_"
        }
    }

    Write-Output "Using User Object ID: $UserId"
    $outputObject.UserId = $UserId

    # Step 3: Get Role Assignments for User at Root Scope using ARM API
    Write-Output "Searching for elevated access role assignments..."
    $roleAssignmentsApi = "$managementApi/providers/Microsoft.Authorization/roleAssignments?api-version=$apiVersion&`$filter=principalId+eq+'$UserId'"
    $roleAssignmentsResponse = Invoke-RestMethod -Uri $roleAssignmentsApi -Method Get -Headers $headersARM

    # Find role assignment for User Access Administrator at "/" scope
    $roleAssignment = $roleAssignmentsResponse.value | Where-Object {
        $_.properties.roleDefinitionId -eq $roleDefinitionId -and $_.properties.scope -eq "/"
    }

    if (-not $roleAssignment) {
        $outputObject.Status = "Completed"
        $outputObject.Message = "No elevated access role assignments found for removal. The user either doesn't have elevated access or it was already removed."
        $outputObject.Success = $true
        return $outputObject
    }

    $roleAssignmentId = $roleAssignment.name
    $outputObject.RoleAssignmentId = $roleAssignmentId

    Write-Output "Found Elevated Access Role Assignment ID: $roleAssignmentId"

    # Step 4: Remove Elevated Access
    $removeRoleApi = "$managementApi/providers/Microsoft.Authorization/roleAssignments/$($roleAssignmentId)?api-version=$apiVersion"

    Write-Output "Removing Elevated Access..."
    Invoke-RestMethod -Uri $removeRoleApi -Method Delete -Headers $headersARM | Out-Null
    
    $outputObject.Status = "Completed"
    $outputObject.Message = "Successfully removed Elevated Access for user"
    $outputObject.Success = $true
    
    Write-Output "$(Get-Timestamp) Successfully removed Elevated Access for user: $UserId"
    Write-Output "The user no longer has User Access Administrator privileges at the root scope."
}
catch {
    $errorMessage = "Failed to remove Elevated Access for user: $UserId. Error: $_"
    Write-Error $errorMessage
    
    $outputObject.Status = "Failed"
    $outputObject.Message = $errorMessage
    $outputObject.Success = $false
}

# Return structured output for Logic App consumption
return $outputObject