Skip to main content

Expiring Assignments Detection

Configure early warning for PIM assignments approaching expiration.

What It Does

PIM Monitor periodically scans for assignments expiring within a configurable window. When found, they're flagged as Medium severity changes in the scan report.

Example:

  • Window: 14 days
  • Current date: 2026-04-27
  • Assignment expires: 2026-05-10 (13 days from now)
  • Result: Flagged as expiring

Configuration

Set the Expiring Window

Set EXPIRING_WINDOW_DAYS in your pipeline variables:

Azure DevOps:

  1. PipelinesPIM MonitorEditVariables
  2. Add: EXPIRING_WINDOW_DAYS = 14

GitHub Actions:

  1. SettingsSecrets and variablesActionsVariables
  2. Add: EXPIRING_WINDOW_DAYS = 14

Window Values

WindowUse caseWarning level
7 daysWeekly reviewsAggressive (short notice)
14 daysDefault; bi-weekly planningBalanced
30 daysMonthly batch renewalsConservative (long notice)
90 daysQuarterly planningVery early notice

Default: 14 days (2 weeks)

How Expiring Assignments Work

Detection Logic

For each PIM assignment (Directory Roles and PIM Groups):

  1. Check if assignment has expiration date: assignments without an end date are skipped
  2. Compare expiration against today + window: if expiring within window, flag it
  3. Classify as Medium severity: expiring assignments warrant review before they lapse
  4. Include in scan report: listed under Medium changes

Types of Assignments Checked

  • Directory Roles:

    • Eligible role assignments (expiration date = activation timeout + current policy duration)
    • Active role assignments (expiration date = next review date)
  • PIM Groups:

    • Eligible member/owner assignments
    • Active member/owner assignments

What Gets Reported

Expiring assignments appear in the scan report as Medium severity changes:

Medium (3)
▼ Directory Roles > Global Administrator > assignments
- Assignment expiring in 13 days (eligible): Jane Doe
- Assignment expiring in 8 days (active): John Smith
▼ PIM Groups > Tier-0-Admins > assignments
- Assignment expiring in 5 days (eligible): Alice Chen

Notification Behavior

Included in Notifications?

Expiring assignments are classified as Medium severity, so they are included at the default threshold:

SettingInclude expiring?Example output
HighNoOnly critical changes
MediumYes (default)Security + config changes, including expiring
LowYesAll changes except metadata
InformationalYesAll changes including metadata

Expiring assignment notifications are on by default, so you do not need to change the threshold.

Separate Reports

Even if notifications are disabled, expiring assignments appear in:

  • inventory/ files (committed to git)
  • HTML scan report (if REPORT_ARTIFACT=true)
  • Pipeline logs

Best Practices

Window Size Selection

Too small (7 days): Too many false alarms, insufficient lead time for renewals Optimal (14 days): Enough notice to plan renewals, not too noisy Too large (90 days): Most assignments appear expiring; loses signal-to-noise

Recommendation: Start with 14, adjust based on your renewal SLA.

Combining with Expected Changes

If you intentionally expire assignments and don't want notifications:

  1. Create expected-changes.json entry:

    {
    "workload": "directory-roles",
    "entity": "global-administrator",
    "fileType": "assignments",
    "reason": "Planned rotation per security review",
    "expiresUtc": "2026-05-10T17:00:00Z"
    }
  2. Set window to 14 days (normal)

  3. Matching expiring assignments won't trigger notifications

  4. Entry auto-deletes after expiration

Regular Review Cycle

Set up a schedule to review expiring assignments:

Set up a review cadence that matches your renewal SLA. With a 14-day window, a weekly check is enough. With 30 days, monthly is fine.

Customizing Expiration Detection

Change Assignment Severity

Edit the Find-ExpiringAssignments function in src/diff.ps1 to change expiring assignments from Medium to a different severity:

Current:

$changes += @{
severity = "Medium"
...
}

Change to:

$changes += @{
severity = "Low" # Or "High" / "Informational"
...
}

Change Detection Window in Code

The window is controlled by the EXPIRING_WINDOW_DAYS variable at runtime. To hardcode a different default, edit src/Scan-PimState.ps1:

$windowDays = if ([int]::TryParse($env:EXPIRING_WINDOW_DAYS, [ref]$parsed)) {
$parsed
} else {
14 # Default if variable unset
}

Add Additional Criteria

To skip assignments from certain roles or groups:

Edit Find-ExpiringAssignments in src/diff.ps1:

function Find-ExpiringAssignments {
param($Assignments, $WindowDays)

# Add filter:
if ($slug -match "^(break-glass|emergency)") {
continue # Skip break-glass accounts
}

# ... rest of logic
}

Troubleshooting

No expiring assignments detected

Check:

  1. Are there any assignments with expiration dates? Permanent assignments ignored
  2. Are expiration dates actually within the window? Check inventory files
  3. Is window size appropriate? If set to 7 days, only finds assignments expiring within week

Too many expiring assignments reported

Check:

  1. Is window too large? (90 days captures ~3 months of expirations)
  2. Do policies have short max durations? (e.g., 30-day assignments = 30 updates per month)
  3. Reduce window: EXPIRING_WINDOW_DAYS = 7

Expiring assignments not in notifications

Check:

  1. Is NOTIFICATION_MIN_SEVERITY set to Medium or lower? (expiring assignments are Medium severity)
  2. Check notifications are enabled (NOTIFICATION_EMAIL or NOTIFICATION_WEBHOOK_URL set)
  3. Review inventory/ files to confirm expiring assignments exist