Auto-Activate All Azure PIM Roles with One PowerShell Script
Category: Azure | Microsoft Entra | PowerShell
Tags: PIM, Privileged Identity Management, PowerShell, Microsoft Graph, Azure AD
The Problem
If your organization uses Azure Privileged Identity Management (PIM), you know the drill:
- Open Azure Portal
- Go to PIM
- Click your role
- Click Activate
- Enter justification
- Set duration
- Click Activate again
- Repeat for every single role
If you have 7–8 eligible roles, that's a painful click-fest every single morning. There had to be a better way.
The Solution
A simple PowerShell script using the Microsoft Graph module that:
- Connects to your Microsoft 365 tenant
- Fetches all your eligible PIM roles automatically
- Reads each role's policy-allowed maximum duration
- Activates all roles in one shot — no prompts, no manual input
Prerequisites
Install the Microsoft Graph PowerShell module if you haven't already:
Install-Module Microsoft.Graph -Scope CurrentUser
The Script
# ============================================================
# Azure PIM Role Activator - Auto Activate All Roles
# Requires: Microsoft.Graph PowerShell Module
# ============================================================
$justification = "Using PowerShell"
Connect-MgGraph -Scopes "RoleManagement.ReadWrite.Directory", "RoleAssignmentSchedule.ReadWrite.Directory" -NoWelcome
$user = Get-MgUser -Filter "userPrincipalName eq '$(( Get-MgContext).Account)'"
Write-Host "Logged in as: $($user.UserPrincipalName)" -ForegroundColor Green
$eligibleRoles = Get-MgRoleManagementDirectoryRoleEligibilitySchedule `
-Filter "principalId eq '$($user.Id)'" -ExpandProperty RoleDefinition
if (-not $eligibleRoles) { Write-Warning "No eligible PIM roles found. Exiting."; exit }
Write-Host "`n===== ACTIVATING ALL ROLES =====" -ForegroundColor Yellow
foreach ($role in $eligibleRoles) {
try {
$rawDuration = ((Get-MgPolicyRoleManagementPolicyAssignment `
-Filter "scopeId eq '/' and scopeType eq 'DirectoryRole' and roleDefinitionId eq '$($role.RoleDefinitionId)'" `
-ExpandProperty "Policy(`$expand=Rules)").Policy.Rules | Where-Object {
$_.Id -eq "Expiration_EndUser_Assignment"
}).AdditionalProperties["maximumDuration"]
$hours = if ($rawDuration -match "PT(\d+)H") { [int]$Matches[1] }
elseif ($rawDuration -match "P(\d+)D") { [int]$Matches[1] * 24 }
else { 8 }
New-MgRoleManagementDirectoryRoleAssignmentScheduleRequest -BodyParameter @{
Action = "selfActivate"
PrincipalId = $user.Id
RoleDefinitionId = $role.RoleDefinitionId
DirectoryScopeId = $role.DirectoryScopeId
Justification = $justification
ScheduleInfo = @{
StartDateTime = (Get-Date).ToUniversalTime()
Expiration = @{ Type = "AfterDuration"; Duration = "PT${hours}H" }
}
} | Out-Null
Write-Host " ✔ Activated : $($role.RoleDefinition.DisplayName) for $hours hour(s)" -ForegroundColor Green
}
catch {
Write-Host " ✘ Failed : $($role.RoleDefinition.DisplayName) — $($_.Exception.Message)" -ForegroundColor Red
}
}
Write-Host "`nDone!" -ForegroundColor Cyan
How It Works — Step by Step
1. Connect to Microsoft Graph
Connect-MgGraph -Scopes "RoleManagement.ReadWrite.Directory", "RoleAssignmentSchedule.ReadWrite.Directory"
This opens a browser login prompt. Sign in with your M365 account. Two permissions are requested:
- RoleManagement.ReadWrite.Directory — to read eligible roles
- RoleAssignmentSchedule.ReadWrite.Directory — to activate them
2. Fetch Your Eligible Roles
Get-MgRoleManagementDirectoryRoleEligibilitySchedule -Filter "principalId eq '$($user.Id)'" -ExpandProperty RoleDefinition
This pulls every role you are eligible for in PIM — the same list you'd see in the Azure Portal under My Roles.
3. Read the Policy Max Duration
Instead of hardcoding a duration like 8 hours, the script reads the PIM policy configured for each role:
Get-MgPolicyRoleManagementPolicyAssignment ... -ExpandProperty "Policy(`$expand=Rules)"
It then looks for the rule named Expiration_EndUser_Assignment and extracts maximumDuration. This is the max hours your admin has configured for that role.
Why does this matter?
Different roles can have different max durations set by your admin. If you hardcode8 hoursbut a role only allows4 hours, the activation fails with anExpirationRuleerror.
The script handles both ISO 8601 formats:
PT4H→ 4 hoursP1D→ 24 hours
4. Activate All Roles
New-MgRoleManagementDirectoryRoleAssignmentScheduleRequest -BodyParameter @{ ... }
This is the Graph API call that does the actual PIM activation — same as clicking Activate in the portal, but done programmatically for every role in a single loop.
Sample Output
Logged in as: sreekanthreddy@contoso.com
===== ACTIVATING ALL ROLES =====
✔ Activated : User Administrator for 8 hour(s)
✔ Activated : Exchange Administrator for 8 hour(s)
✔ Activated : SharePoint Administrator for 8 hour(s)
✔ Activated : Teams Administrator for 8 hour(s)
✔ Activated : Groups Administrator for 4 hour(s)
✔ Activated : Power Platform Administrator for 8 hour(s)
✔ Activated : Office Apps Administrator for 8 hour(s)
Done!
Notice Groups Administrator activated for only 4 hours — that's because its PIM policy max is 4 hours. The script respected it automatically instead of failing.
Customization
| What to change | Where |
|---|---|
| Justification text | $justification = "Using PowerShell" |
| Fallback duration (if policy unreadable) | else { 8 } at the end of the hours block |
Common Error & Fix
Error: ExpirationRule — RoleAssignmentRequestPolicyValidationFailed
Cause: You requested more hours than the role's PIM policy allows.
Fix: Use this script — it reads the policy max per role and uses that automatically.
Conclusion
This script cuts your daily PIM activation from a 5-minute portal click-fest down to a single PowerShell run. It's policy-aware, handles multiple roles gracefully, and clearly tells you what was activated and for how long.
Save it, schedule it, or just run it each morning. Either way — one command and you're done.
#
============================================================
# Azure PIM Role
Activator
# Requires:
Microsoft.Graph PowerShell Module
# Install :
Install-Module Microsoft.Graph -Scope CurrentUser
#
============================================================
# ---------- CONFIGURATION ----------
$justification = "Activating role via PowerShell
script"
$durationHours = 8 #
Set activation duration (hours)
# -----------------------------------
# Step 1: Install Graph module if missing
if (-not (Get-Module -ListAvailable -Name Microsoft.Graph))
{
Write-Host "Installing
Microsoft.Graph module..." -ForegroundColor Yellow
Install-Module Microsoft.Graph
-Scope CurrentUser -Force
}
# Step 2: Connect to Microsoft Graph
Write-Host "`nConnecting to Microsoft Graph..." -ForegroundColor
Cyan
Connect-MgGraph -Scopes "RoleManagement.ReadWrite.Directory",
"RoleAssignmentSchedule.ReadWrite.Directory" -NoWelcome
# Step 3: Get current user
$userId = (Get-MgContext).Account
$user = Get-MgUser -Filter
"userPrincipalName eq '$userId'"
Write-Host "Logged in as: $userId" -ForegroundColor
Green
# Step 4: Get all eligible roles for the user
Write-Host "`nFetching eligible PIM roles..." -ForegroundColor
Cyan
$eligibleRoles = Get-MgRoleManagementDirectoryRoleEligibilitySchedule
`
-Filter "principalId
eq '$($user.Id)'" -ExpandProperty RoleDefinition
if (-not $eligibleRoles) {
Write-Warning "No
eligible PIM roles found for this user. Exiting."
exit
}
# Step 5: Display eligible roles
Write-Host "`n===== ELIGIBLE ROLES =====" -ForegroundColor
Yellow
$index = 1
$roleList = @()
foreach ($role in $eligibleRoles) {
$roleName = $role.RoleDefinition.DisplayName
$scopeId = $role.DirectoryScopeId
Write-Host " [$index] $roleName (Scope: $scopeId)"
$roleList += [PSCustomObject]@{
Index = $index
RoleName = $roleName
RoleDefinitionId = $role.RoleDefinitionId
DirectoryScopeId = $scopeId
}
$index++
}
# Step 6: Prompt user to select roles
Write-Host "`nEnter role numbers to activate
(comma-separated), or type 'ALL' to activate all:"
$input = Read-Host "Your selection"
if ($input.Trim().ToUpper() -eq "ALL") {
$selectedRoles = $roleList
} else {
$selectedIndexes =
$input -split "," | ForEach-Object { $_.Trim() -as [int] }
$selectedRoles = $roleList | Where-Object { $_.Index -in $selectedIndexes
}
}
if (-not $selectedRoles) {
Write-Warning "No
valid roles selected. Exiting."
exit
}
# Step 7: Activate selected roles
Write-Host "`n===== ACTIVATING ROLES =====" -ForegroundColor
Yellow
foreach ($role in $selectedRoles) {
try {
$params = @{
Action = "selfActivate"
PrincipalId = $user.Id
RoleDefinitionId = $role.RoleDefinitionId
DirectoryScopeId = $role.DirectoryScopeId
Justification = $justification
ScheduleInfo = @{
StartDateTime = (Get-Date).ToUniversalTime()
Expiration = @{
Type = "AfterDuration"
Duration = "PT${durationHours}H"
}
}
}
New-MgRoleManagementDirectoryRoleAssignmentScheduleRequest
-BodyParameter $params | Out-Null
Write-Host " ✔ Activated : $($role.RoleName)
for $durationHours hour(s)" -ForegroundColor Green
}
catch {
Write-Host " ✘ Failed : $($role.RoleName) — $($_.Exception.Message)"
-ForegroundColor Red
}
}
Write-Host "`nDone! Active roles will expire after $durationHours
hour(s)." -ForegroundColor Cyan
No comments:
Post a Comment