Skip to content

Lab 07: PIM - CLI/PowerShell Solution

Note: PIM has excellent PowerShell/Graph API support for automation


Prerequisites

powershell
# Install Microsoft Graph PowerShell SDK
Install-Module Microsoft.Graph -Scope CurrentUser -Force

# Connect with required scopes for PIM
Connect-MgGraph -Scopes @(
    "RoleManagement.ReadWrite.Directory",
    "RoleAssignmentSchedule.ReadWrite.Directory",
    "RoleEligibilitySchedule.ReadWrite.Directory",
    "PrivilegedAccess.ReadWrite.AzureResources",
    "Directory.ReadWrite.All"
)

# Verify connection
Get-MgContext

Task 1: Explore PIM Dashboard

List All Entra ID Roles

powershell
# Get all directory role definitions
Get-MgRoleManagementDirectoryRoleDefinition | 
    Select-Object DisplayName, Id, IsBuiltIn |
    Sort-Object DisplayName |
    Format-Table

# Get specific role ID (you'll need this later)
$globalAdminRole = Get-MgRoleManagementDirectoryRoleDefinition -Filter "displayName eq 'Global Administrator'"
Write-Host "Global Administrator Role ID: $($globalAdminRole.Id)"

$userAdminRole = Get-MgRoleManagementDirectoryRoleDefinition -Filter "displayName eq 'User Administrator'"
Write-Host "User Administrator Role ID: $($userAdminRole.Id)"

List Current Role Assignments

powershell
# Get all active role assignments
Get-MgRoleManagementDirectoryRoleAssignment -All | ForEach-Object {
    $role = Get-MgRoleManagementDirectoryRoleDefinition -UnifiedRoleDefinitionId $_.RoleDefinitionId
    $principal = Get-MgDirectoryObject -DirectoryObjectId $_.PrincipalId
    
    [PSCustomObject]@{
        Role = $role.DisplayName
        Principal = $principal.AdditionalProperties.displayName
        Scope = $_.DirectoryScopeId
    }
} | Format-Table -AutoSize

# Get all eligible (PIM) role assignments
Get-MgRoleManagementDirectoryRoleEligibilitySchedule -All | ForEach-Object {
    $role = Get-MgRoleManagementDirectoryRoleDefinition -UnifiedRoleDefinitionId $_.RoleDefinitionId
    $principal = Get-MgDirectoryObject -DirectoryObjectId $_.PrincipalId
    
    [PSCustomObject]@{
        Role = $role.DisplayName
        Principal = $principal.AdditionalProperties.displayName
        StartDateTime = $_.StartDateTime
        EndDateTime = $_.EndDateTime
        Status = $_.Status
    }
} | Format-Table -AutoSize

Task 2: Configure PIM Role Settings

Get Current Role Settings

powershell
# Get role policy assignment (PIM settings) for Global Administrator
$globalAdminRole = Get-MgRoleManagementDirectoryRoleDefinition -Filter "displayName eq 'Global Administrator'"

$policyAssignment = Get-MgPolicyRoleManagementPolicyAssignment -Filter "scopeId eq '/' and scopeType eq 'DirectoryRole' and roleDefinitionId eq '$($globalAdminRole.Id)'"

# Get the policy rules
$policy = Get-MgPolicyRoleManagementPolicy -UnifiedRoleManagementPolicyId $policyAssignment.PolicyId
Get-MgPolicyRoleManagementPolicyRule -UnifiedRoleManagementPolicyId $policyAssignment.PolicyId |
    Select-Object Id, @{N='Type';E={$_.'@odata.type'}} |
    Format-Table

Update Role Settings (Advanced)

powershell
# Update PIM settings for a role
# This is complex - each rule type needs specific update

$globalAdminRole = Get-MgRoleManagementDirectoryRoleDefinition -Filter "displayName eq 'Global Administrator'"
$policyAssignment = Get-MgPolicyRoleManagementPolicyAssignment -Filter "scopeId eq '/' and scopeType eq 'DirectoryRole' and roleDefinitionId eq '$($globalAdminRole.Id)'"
$policyId = $policyAssignment.PolicyId

# Get current rules
$rules = Get-MgPolicyRoleManagementPolicyRule -UnifiedRoleManagementPolicyId $policyId

# Example: Update expiration rule for activation (max duration)
$expirationRule = $rules | Where-Object { $_.Id -eq "Expiration_EndUser_Assignment" }

$params = @{
    "@odata.type" = "#microsoft.graph.unifiedRoleManagementPolicyExpirationRule"
    id = "Expiration_EndUser_Assignment"
    isExpirationRequired = $true
    maximumDuration = "PT4H"  # 4 hours in ISO 8601 format
}

Update-MgPolicyRoleManagementPolicyRule `
    -UnifiedRoleManagementPolicyId $policyId `
    -UnifiedRoleManagementPolicyRuleId "Expiration_EndUser_Assignment" `
    -BodyParameter $params

Task 3: Create an Eligible Assignment

PowerShell - Create Eligible Assignment

powershell
# Get the role definition ID
$userAdminRole = Get-MgRoleManagementDirectoryRoleDefinition -Filter "displayName eq 'User Administrator'"
$roleId = $userAdminRole.Id

# Get the user's ID
$user = Get-MgUser -Filter "userPrincipalName eq 'user1@yourdomain.com'"
$userId = $user.Id

# Create eligible assignment request
$params = @{
    action = "adminAssign"
    justification = "Assigning eligible role for lab exercise"
    roleDefinitionId = $roleId
    directoryScopeId = "/"
    principalId = $userId
    scheduleInfo = @{
        startDateTime = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
        expiration = @{
            type = "afterDateTime"
            endDateTime = (Get-Date).AddMonths(6).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
        }
    }
}

New-MgRoleManagementDirectoryRoleEligibilityScheduleRequest -BodyParameter $params

Verify the Assignment

powershell
# List eligible assignments for the user
$userId = (Get-MgUser -Filter "userPrincipalName eq 'user1@yourdomain.com'").Id

Get-MgRoleManagementDirectoryRoleEligibilitySchedule -Filter "principalId eq '$userId'" |
    ForEach-Object {
        $role = Get-MgRoleManagementDirectoryRoleDefinition -UnifiedRoleDefinitionId $_.RoleDefinitionId
        [PSCustomObject]@{
            Role = $role.DisplayName
            StartDateTime = $_.StartDateTime
            EndDateTime = $_.EndDateTime
            Status = $_.Status
        }
    } | Format-Table -AutoSize

Task 4: Configure User Administrator Role Settings

powershell
# This follows the same pattern as Task 2
# Get role and policy
$userAdminRole = Get-MgRoleManagementDirectoryRoleDefinition -Filter "displayName eq 'User Administrator'"
$policyAssignment = Get-MgPolicyRoleManagementPolicyAssignment -Filter "scopeId eq '/' and scopeType eq 'DirectoryRole' and roleDefinitionId eq '$($userAdminRole.Id)'"
$policyId = $policyAssignment.PolicyId

# Update maximum activation duration to 8 hours
$params = @{
    "@odata.type" = "#microsoft.graph.unifiedRoleManagementPolicyExpirationRule"
    id = "Expiration_EndUser_Assignment"
    isExpirationRequired = $true
    maximumDuration = "PT8H"  # 8 hours
}

Update-MgPolicyRoleManagementPolicyRule `
    -UnifiedRoleManagementPolicyId $policyId `
    -UnifiedRoleManagementPolicyRuleId "Expiration_EndUser_Assignment" `
    -BodyParameter $params

Write-Host "Updated User Administrator role settings"

Task 5: Activate a Role

As the Eligible User - Request Activation

powershell
# Connect as the eligible user (User1)
Connect-MgGraph -Scopes "RoleAssignmentSchedule.ReadWrite.Directory"

# Get my eligible roles
Get-MgRoleManagementDirectoryRoleEligibilitySchedule -Filter "principalId eq '$((Get-MgContext).Account)'"

# Request role activation
$userAdminRole = Get-MgRoleManagementDirectoryRoleDefinition -Filter "displayName eq 'User Administrator'"

$params = @{
    action = "selfActivate"
    principalId = (Get-MgUser -UserId (Get-MgContext).Account).Id
    roleDefinitionId = $userAdminRole.Id
    directoryScopeId = "/"
    justification = "Creating test users for Project Alpha development environment"
    scheduleInfo = @{
        startDateTime = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
        expiration = @{
            type = "afterDuration"
            duration = "PT2H"  # 2 hours
        }
    }
}

New-MgRoleManagementDirectoryRoleAssignmentScheduleRequest -BodyParameter $params

Check Activation Status

powershell
# Get my active role assignments
Get-MgRoleManagementDirectoryRoleAssignmentSchedule -Filter "principalId eq '$((Get-MgContext).Account)'" |
    ForEach-Object {
        $role = Get-MgRoleManagementDirectoryRoleDefinition -UnifiedRoleDefinitionId $_.RoleDefinitionId
        [PSCustomObject]@{
            Role = $role.DisplayName
            Status = $_.Status
            StartDateTime = $_.StartDateTime
            EndDateTime = $_.EndDateTime
        }
    } | Format-Table -AutoSize

Task 6: PIM for Azure Resources

Azure CLI for Azure RBAC PIM

bash
# List eligible role assignments for Azure resources
az role assignment list --assignee "user2@yourdomain.com" --include-inherited --output table

# Using REST API for PIM Azure Resources
az rest --method GET \
    --uri "https://management.azure.com/providers/Microsoft.Authorization/roleEligibilityScheduleRequests?api-version=2020-10-01" \
    --output json

PowerShell for Azure PIM

powershell
# Connect to Azure
Connect-AzAccount

# Get eligible assignments at subscription level
$subscriptionId = (Get-AzContext).Subscription.Id

# List role eligibility schedules
$uri = "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Authorization/roleEligibilitySchedules?api-version=2020-10-01"

$token = (Get-AzAccessToken).Token
$headers = @{
    Authorization = "Bearer $token"
    "Content-Type" = "application/json"
}

$response = Invoke-RestMethod -Uri $uri -Headers $headers -Method GET
$response.value | ForEach-Object {
    [PSCustomObject]@{
        RoleId = $_.properties.roleDefinitionId.Split('/')[-1]
        PrincipalId = $_.properties.principalId
        Status = $_.properties.status
        EndDateTime = $_.properties.endDateTime
    }
} | Format-Table

Task 7: Approve/Deny PIM Requests

List Pending Approval Requests

powershell
# Get pending approval requests for Entra ID roles
$pendingRequests = Get-MgRoleManagementDirectoryRoleAssignmentScheduleRequest -Filter "status eq 'PendingApproval'"

$pendingRequests | ForEach-Object {
    $role = Get-MgRoleManagementDirectoryRoleDefinition -UnifiedRoleDefinitionId $_.RoleDefinitionId
    $principal = Get-MgUser -UserId $_.PrincipalId
    
    [PSCustomObject]@{
        RequestId = $_.Id
        Role = $role.DisplayName
        Requester = $principal.DisplayName
        Justification = $_.Justification
        RequestedOn = $_.CreatedDateTime
    }
} | Format-Table -AutoSize

Approve a Request

powershell
# Note: Approval requires the Identity Governance API
# This is a complex operation involving approval steps

$requestId = "your-request-id"

# Get the approval object
$approval = Get-MgIdentityGovernancePrivilegedAccessGroupAssignmentApproval -ApprovalId $requestId

# Approve
$params = @{
    reviewResult = "Approve"
    justification = "Approved for project work"
}

# Update the approval stage
# This requires specific API calls based on the approval workflow

Task 8: Create Access Review

PowerShell - Create Access Review

powershell
# Create an access review for User Administrator role
$userAdminRole = Get-MgRoleManagementDirectoryRoleDefinition -Filter "displayName eq 'User Administrator'"

$params = @{
    displayName = "Quarterly User Admin Review"
    descriptionForAdmins = "Quarterly review of User Administrator role assignments"
    descriptionForReviewers = "Please review whether these users still need User Administrator access"
    scope = @{
        "@odata.type" = "#microsoft.graph.principalResourceMembershipsScope"
        principalScopes = @(
            @{
                "@odata.type" = "#microsoft.graph.accessReviewQueryScope"
                query = "/roleManagement/directory/roleAssignments?`$filter=roleDefinitionId eq '$($userAdminRole.Id)'"
                queryType = "MicrosoftGraph"
            }
        )
        resourceScopes = @(
            @{
                "@odata.type" = "#microsoft.graph.accessReviewQueryScope"
                query = "/roleManagement/directory/roleDefinitions/$($userAdminRole.Id)"
                queryType = "MicrosoftGraph"
            }
        )
    }
    reviewers = @(
        @{
            query = "/users/me"
            queryType = "MicrosoftGraph"
        }
    )
    settings = @{
        mailNotificationsEnabled = $true
        reminderNotificationsEnabled = $true
        justificationRequiredOnApproval = $true
        defaultDecisionEnabled = $true
        defaultDecision = "Deny"
        instanceDurationInDays = 14
        autoApplyDecisionsEnabled = $true
        recommendationsEnabled = $true
        recurrence = @{
            pattern = @{
                type = "absoluteMonthly"
                interval = 3  # Quarterly
            }
            range = @{
                type = "noEnd"
                startDate = (Get-Date).ToString("yyyy-MM-dd")
            }
        }
    }
}

New-MgIdentityGovernanceAccessReviewDefinition -BodyParameter $params

Task 9: Review PIM Audit Logs

Get PIM Audit Events

powershell
# Get PIM-related audit logs
$startDate = (Get-Date).AddDays(-30).ToString("yyyy-MM-ddTHH:mm:ssZ")

Get-MgAuditLogDirectoryAudit -Filter "activityDateTime ge $startDate" -Top 100 | 
    Where-Object { $_.Category -eq "RoleManagement" } |
    Select-Object ActivityDateTime, 
                  ActivityDisplayName,
                  @{N='Actor';E={$_.InitiatedBy.User.UserPrincipalName}},
                  Result,
                  @{N='Target';E={$_.TargetResources[0].DisplayName}} |
    Format-Table -AutoSize

Export to CSV

powershell
# Export PIM audit logs for compliance
$auditLogs = Get-MgAuditLogDirectoryAudit -Filter "category eq 'RoleManagement'" -Top 1000

$auditLogs | Select-Object `
    ActivityDateTime,
    ActivityDisplayName,
    @{N='Actor';E={$_.InitiatedBy.User.UserPrincipalName}},
    @{N='ActorIP';E={$_.InitiatedBy.User.IpAddress}},
    Result,
    ResultReason,
    @{N='Target';E={$_.TargetResources[0].DisplayName}} |
    Export-Csv -Path "PIM_Audit_$(Get-Date -Format 'yyyyMMdd').csv" -NoTypeInformation

Write-Host "Exported audit logs to PIM_Audit_$(Get-Date -Format 'yyyyMMdd').csv"

Task 10: Check PIM Alerts

List PIM Alerts

powershell
# Get PIM alerts
Get-MgPrivilegedAccessResourceRoleSettingAlert -PrivilegedAccessId "aadRoles" |
    Select-Object AlertType, AlertDescription, IsActive |
    Format-Table -AutoSize

# Alternative: Use direct API call
$uri = "https://graph.microsoft.com/v1.0/privilegedAccess/aadRoles/resources/tenant/alerts"
Invoke-MgGraphRequest -Uri $uri -Method GET

Challenge: List Over-Activated Users

powershell
# Find users who have had roles active for extended periods
$activationHistory = Get-MgAuditLogDirectoryAudit -Filter "activityDisplayName eq 'Add member to role completed (PIM activation)'" -Top 500

$userActivations = $activationHistory | Group-Object -Property {$_.InitiatedBy.User.UserPrincipalName}

$userActivations | ForEach-Object {
    [PSCustomObject]@{
        User = $_.Name
        ActivationCount = $_.Count
        LastActivation = ($_.Group | Sort-Object ActivityDateTime -Descending | Select-Object -First 1).ActivityDateTime
    }
} | Sort-Object ActivationCount -Descending | Format-Table -AutoSize

Summary: CLI/PowerShell Capabilities

TaskPowerShell/CLI SupportNotes
List role definitions✅ FullEasy
List eligible/active assignments✅ FullEasy
Create eligible assignments✅ FullSupported
Configure role settings⚠️ ComplexRequires policy rule updates
Activate roles✅ FullselfActivate action
Process approvals⚠️ LimitedComplex API
Create access reviews✅ FullSupported
View audit logs✅ FullEasy
Manage alerts⚠️ LimitedSome API support

Released under the MIT License.