invoke-EntraIDPimElevations.ps1


<#PSScriptInfo
 
.VERSION 1.2
 
.GUID 78e7abb6-aeae-4486-81e8-9b61e8b2e3e3
 
.AUTHOR JanKetilSkanke
 
.COMPANYNAME CloudWay
 
.COPYRIGHT
 
.TAGS
 
.LICENSEURI
 
.PROJECTURI
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES Microsoft.Graph.Users, Microsoft.Graph.Identity.Governance
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
 
.PRIVATEDATA
 
#>


<#
.SYNOPSIS
    Name: invoke-EntraIDPimElevations.ps1
    Script to activate Eligible roles in Entra ID PIM for a user
 
.DESCRIPTION
    Script to activate Eligible roles in Entra ID PIM for a user
 
.PARAMETER RolesToActivate
    Comma separated list of roles to activate, Example -RolesToActivate "Global Reader, Intune Administrator"
 
.PARAMETER Justification
    Justification for activating the roles
 
.PARAMETER ActivationDuration
    Duration in hours for the activation, default is 8 hours
 
.PARAMETER GetAvailableRoles
    Switch to get all available eligible roles for the user
 
.PARAMETER Consent
    Switch to request consent on all required scopes
 
.EXAMPLE
  invoke-EntraIDPimElevations.ps1 -RolesToActivate "Global Reader, Intune Administrator" -Justification "Testing" -ActivationDuration 4
  invoke-EntraIDPimElevations.ps1 -GetAvailableRoles
  invoke-EntraIDPimElevations.ps1 -Consent
 
.NOTES
    Version: 1.2
    Author: Jan Ketil Skanke (@JankeSkanke)
    Creation Date: 2023-12-13
    Author: Jan Ketil Skanke
    Contact: @JankeSkanke
    Created: 2023-12-13
    Version history:
    1.1.0 - (2023-12-13) Script Created
    1.2.0 - (2023-12-15) Added consent switch and cleaned up code
#>

#Requires -Modules Microsoft.Graph.Identity.Governance, Microsoft.Graph.Users
param (
    [Parameter(Mandatory=$true, ParameterSetName='ActivateRoles', HelpMessage="Specifies the roles to activate.")]
    [ValidateNotNullOrEmpty()]
    [string]$RolesToActivate,
    [Parameter(Mandatory=$true, ParameterSetName='ActivateRoles', HelpMessage="Specifies justification for activation.")]
    [ValidateNotNullOrEmpty()]
    [ValidateLength(6, 200)]
    [string]$Justification,
    [Parameter(Mandatory=$false, ParameterSetName='ActivateRoles', HelpMessage="Specifies the duration of the activation.")]
    [int]$ActivationDuration = 8,
    [Parameter(Mandatory=$true, ParameterSetName='GetRoles', HelpMessage="Get all available eligible roles for the user.")]
    [switch]$GetAvailableRoles,
    [Parameter(Mandatory=$true, ParameterSetName='Consent', HelpMessage="Request consent on scopes.")]
    [switch]$Consent
)
# Clear out any existing tokens
Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null

# Check if consent is requested and request it if true
if ($Consent) {
    try {
        Connect-MgGraph -Scopes "RoleEligibilitySchedule.Read.Directory, RoleAssignmentSchedule.ReadWrite.Directory, User.Read.All" -NoWelcome -ErrorAction Stop
        Write-Host "Consent OK, exiting script" -ForegroundColor Green
        Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null
    exit      
    }
    catch {
        Write-Host "Consent not granted, admin request might be needed" -ForegroundColor Yellow
        Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null
        exit
    }
}

# Connect to Graph
try {
    Connect-MgGraph -NoWelcome -ErrorAction Stop
    # Get auth context
    $MyContext = Get-MgContext
    $Me = (Get-MgUser -UserId $MyContext.Account).Id
    if ($MyContext.Scopes -notcontains "RoleEligibilitySchedule.Read.Directory" -or $MyContext.Scopes -notcontains "RoleAssignmentSchedule.ReadWrite.Directory" -or $MyContext.Scopes -notcontains "User.Read.All" ) {
        Write-Warning "Missing consent, run script with -Consent switch to request consent"
        Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null
        exit
    }
    Write-Output "Connected as $($MyContext.Account)"     
}
catch {
    Write-Warning "Failed to connect to Graph: $($_.Exception.Message), exiting script"
    Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null
    exit
}
# Try to get all eligible roles for user for processing
try {
    $myRoles = Get-MgRoleManagementDirectoryRoleEligibilitySchedule -ExpandProperty RoleDefinition -All -Filter "principalId eq '$Me'" -ErrorAction Stop
}
catch {
    Write-Warning "Failed to get eligible roles: $($_.Exception.Message), exiting script"
    Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null
    exit
}

switch ($PSCmdlet.ParameterSetName) {
    "GetRoles"{
        if ($($myroles.count) -eq 0) {
            Write-Warning "No eligible roles found, exiting script"
            break
        }
        Write-Output "You have $($myroles.count) eligible roles and they are:"
        Write-Output $($myRoles.RoleDefinition.DisplayName | Sort-Object | Format-Table)
        break
    }
    "ActivateRoles"{
        if ($($myroles.count) -eq 0) {
            Write-Warning "No eligible roles found, exiting script"
            break
        }
        $MyRolesNames = $myRoles.RoleDefinition.DisplayName
        $Roles = $RolesToActivate.Split(",") | ForEach-Object { $_.Trim() }
        foreach ($role in $Roles) {
            if ($role -notin $MyRolesNames) {
                Write-Warning "Role $role is not eligible for you to activate, exiting script"
                exit
            }
        }
        Write-Host "You are about to activate the following roles:" -ForegroundColor Green
        Write-Output $Roles | Sort-Object | Format-Table
        Write-Output "This will be active for $($ActivationDuration) hours"
        Write-Host "Are you sure you want to continue? (Y/N)" -ForegroundColor Yellow
        $answer = Read-Host
        if ($answer -ne "Y") {
            Write-Output "Exiting script"
            break
        }
        Write-Output "Activating roles: $RolesToActivate"
        Write-Output "Reason: $Justification"
        
        # Activate the roles
        foreach ($role in $Roles) {
            $myRole = $myroles | Where-Object {$_.RoleDefinition.DisplayName -eq $role}
            #Write-Output $myRole
            $params = @{
                Action = "selfActivate"
                PrincipalId = $myRole.PrincipalId
                RoleDefinitionId = $myRole.RoleDefinitionId
                DirectoryScopeId = $myRole.DirectoryScopeId
                Justification = "Enable $role role with reason $Justification"
                ScheduleInfo = @{
                    StartDateTime = Get-Date
                    Expiration = @{
                        Type = "AfterDuration"
                        Duration = "PT$($ActivationDuration)H"
                    }
                }
            }
            try {
                $Activation = New-MgRoleManagementDirectoryRoleAssignmentScheduleRequest -BodyParameter $params -ErrorAction Stop
                Write-Output "Activated role $($role) for $($ActivationDuration) hours from UTC $($Activation.ScheduleInfo.StartDateTime) and will expire at UTC $($Activation.ScheduleInfo.StartDateTime.AddHours($ActivationDuration))" 
            }
            catch {
                <#Do this if a terminating exception happens#>
                Write-Warning "Error activating role $($role): $($_.Exception.Message)"
                
            }
        }
    }
    Default {exit}
}
Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null