PimRoleTools.psm1

function Show-PimRole {
<#!
.SYNOPSIS
Shows a human-friendly summary of a specific PIM role assignment for the current user.
.DESCRIPTION
Displays the status, start/end time, and time remaining for a given Azure AD PIM role. Uses Get-PimRole internally.
.PARAMETER RoleName
The name of the role to show the status for. Defaults to 'Helpdesk Administrator'.
.EXAMPLE
Show-PimRole -RoleName "Global Administrator"
#>

    [CmdletBinding()]
    param (
        [string]$RoleName = "Helpdesk Administrator"
    )

    $role = Get-PimRole -RoleName $RoleName
    if (-not $role) {
        Write-Host "ℹ️ You do not currently have any assignment for '$RoleName'."
        return
    }

    $r = $role | Select-Object -First 1
    Write-Host "🛡️ Role: $($r.RoleName)"
    Write-Host "Status : $($r.Status)"
    if ($r.Status -eq 'Active') {
        Write-Host "Start Time : $($r.StartTime)"
        Write-Host "End Time : $($r.EndTime)"
        Write-Host "Time Remaining: $($r.TimeRemaining)"
    } elseif ($r.Status -eq 'Permanent') {
        Write-Host "Assignment is permanent or direct, not time-limited."
    } elseif ($r.Status -eq 'Eligible') {
        Write-Host "You are eligible for this role, but it is not currently active."
    }
}

function Enable-PimRole {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string]$RoleName,

        [string]$Justification = "Enable $RoleName for administrative task",

        [string]$Duration = "PT8H"
    )

    if (-not (Get-MgContext)) {
        Connect-MgGraph -Scopes "RoleManagement.ReadWrite.Directory", "PrivilegedAccess.ReadWrite.AzureAD", "Directory.Read.All"
    }

    $currentUserId = (Get-MgUser -UserId (Get-MgContext).Account).Id

    $eligibleRoles = Get-MgRoleManagementDirectoryRoleEligibilitySchedule -All -ExpandProperty RoleDefinition `
        -Filter "principalId eq '$currentUserId'"

    $myRole = $eligibleRoles | Where-Object { $_.RoleDefinition.DisplayName -eq $RoleName }

    if (-not $myRole) {
        Write-Host "❌ No eligible PIM assignment found for '$RoleName'." -ForegroundColor Red
        return
    }

    $params = @{
        Action           = "selfActivate"
        PrincipalId      = $myRole.PrincipalId
        RoleDefinitionId = $myRole.RoleDefinitionId
        DirectoryScopeId = $myRole.DirectoryScopeId
        Justification    = $Justification
        ScheduleInfo     = @{
            StartDateTime = (Get-Date).ToUniversalTime()
            Expiration    = @{
                Type     = "AfterDuration"
                Duration = $Duration
            }
        }
    }

    try {
        New-MgRoleManagementDirectoryRoleAssignmentScheduleRequest -BodyParameter $params
        Write-Host "✅ PIM activation request submitted for '$RoleName'." -ForegroundColor Green
        # Spinner animation while waiting for activation
        $spinner = @('|','/','-','\')
        $i = 0
        Write-Host -NoNewline "Waiting for activation "
        do {
            Start-Sleep -Seconds 2
            $active = Get-PimRole -Status Active -RoleName $RoleName
            Write-Host -NoNewline ("`b" + $spinner[$i % $spinner.Length])
            $i++
        } while (-not $active)
        Write-Host "`b✔️ Activated!"
    } catch {
        Write-Host "❌ Failed to activate PIM role: $_" -ForegroundColor Red
    }
}

function Get-PimRole {
<#!
.SYNOPSIS
Gets all PIM role assignments for the current user, with status and timing details.
.DESCRIPTION
Lists all Azure AD PIM roles (active, eligible, permanent) for the current user. Can filter by role name and status. Outputs objects for scripting.
.PARAMETER RoleName
Filter by role name.
.PARAMETER Status
Filter by status: Active, Eligible, Permanent, or All (default).
.EXAMPLE
Get-PimRole
.EXAMPLE
Get-PimRole -Status Active
.EXAMPLE
Get-PimRole -RoleName "Global Administrator"
#>

    [CmdletBinding()]
    param (
        [string]$RoleName,
        [ValidateSet('Active','Eligible','Permanent','All')]
        [string]$Status = 'All'
    )

    if (-not (Get-MgContext)) {
        Connect-MgGraph -Scopes "RoleManagement.Read.Directory", "PrivilegedAccess.Read.AzureAD", "Directory.Read.All"
    }

    $currentUserId = (Get-MgUser -UserId (Get-MgContext).Account).Id

    $assignments = Get-MgRoleManagementDirectoryRoleAssignment -All -ExpandProperty RoleDefinition `
        -Filter "principalId eq '$currentUserId'"

    $scheduleInstances = Get-MgRoleManagementDirectoryRoleAssignmentScheduleInstance -All -ExpandProperty RoleDefinition,ActivatedUsing `
        -Filter "principalId eq '$currentUserId'"

    $results = @()
    # Add all active assignments from schedule instances
    foreach ($schedule in $scheduleInstances) {
        $roleName = $schedule.RoleDefinition.DisplayName
        $startTime = $schedule.StartDateTime
        $endTime = $schedule.EndDateTime
        $remaining = $null
        if ($endTime) {
            $remaining = [DateTime]::Parse($endTime).ToLocalTime() - (Get-Date)
        }
        if ($startTime -or $endTime) {
            $results += [PSCustomObject]@{
                RoleName = $roleName
                Status = 'Active'
                StartTime = if ($startTime) { [DateTime]::Parse($startTime).ToLocalTime() } else { $null }
                EndTime = if ($endTime) { [DateTime]::Parse($endTime).ToLocalTime() } else { $null }
                TimeRemaining = $remaining
            }
        } else {
            $results += [PSCustomObject]@{
                RoleName = $roleName
                Status = 'Permanent'
                StartTime = $null
                EndTime = $null
                TimeRemaining = $null
            }
        }
    }
    # Add all assignments (permanent or eligible)
    foreach ($assignment in $assignments) {
        $roleName = $assignment.RoleDefinition.DisplayName
        # Skip if already added as active
        $alreadyActive = $results | Where-Object { $_.RoleName -eq $roleName -and $_.Status -eq 'Active' }
        if ($assignment.ActivatedUsing) {
            if (-not $alreadyActive) {
                $startTime = $assignment.ActivatedUsing.StartDateTime
                $duration = $assignment.ActivatedUsing.Expiration.Duration
                $endTime = $null
                $remaining = $null
                if ($duration) {
                    try {
                        $durationTimespan = [System.Xml.XmlConvert]::ToTimeSpan($duration)
                        $endTime = $startTime.Add($durationTimespan)
                        $remaining = $endTime.ToLocalTime() - (Get-Date)
                    } catch {}
                }
                if ($startTime -or $endTime) {
                    $results += [PSCustomObject]@{
                        RoleName = $roleName
                        Status = 'Active'
                        StartTime = if ($startTime) { [DateTime]::Parse($startTime).ToLocalTime() } else { $null }
                        EndTime = if ($endTime) { [DateTime]::Parse($endTime).ToLocalTime() } else { $null }
                        TimeRemaining = $remaining
                    }
                } else {
                    $results += [PSCustomObject]@{
                        RoleName = $roleName
                        Status = 'Permanent'
                        StartTime = $null
                        EndTime = $null
                        TimeRemaining = $null
                    }
                }
            }
        } elseif ($assignment.AdditionalProperties.Count -eq 0) {
            $results += [PSCustomObject]@{
                RoleName = $roleName
                Status = 'Permanent'
                StartTime = $null
                EndTime = $null
                TimeRemaining = $null
            }
        } else {
            # Only add eligible if not already active or permanent
            $alreadyPermanent = $results | Where-Object { $_.RoleName -eq $roleName -and $_.Status -eq 'Permanent' }
            if (-not $alreadyActive -and -not $alreadyPermanent) {
                $results += [PSCustomObject]@{
                    RoleName = $roleName
                    Status = 'Eligible'
                    StartTime = $null
                    EndTime = $null
                    TimeRemaining = $null
                }
            }
        }
    }
    if ($RoleName) {
        $results = $results | Where-Object { $_.RoleName -eq $RoleName }
    }
    if ($Status -ne 'All') {
        $results = $results | Where-Object { $_.Status -eq $Status }
    }
    $results
}

Export-ModuleMember -Function Show-PimRole, Enable-PimRole, Get-PimRole