en-US/about_Omnicit.PIM.help.txt

TOPIC
    about_Omnicit.PIM

SHORT DESCRIPTION
    Entra ID Privileged Identity Management (PIM) Self Activation Commands for
    Directory Roles, Azure Resources, and Entra ID Groups.

LONG DESCRIPTION
    Omnicit.PIM is a PowerShell 7.4+ (Core-only) module for self-service
    activation and deactivation of PIM roles and group memberships across three
    surfaces:

    Surface Noun Example cmdlet
    ------- ---- --------------
    Directory Roles DirectoryRole Enable-OPIMDirectoryRole
    Azure RBAC Roles AzureRole Enable-OPIMAzureRole
    Entra ID Groups EntraIDGroup Enable-OPIMEntraIDGroup

    All cmdlets use the canonical prefix OPIM and short PIM aliases are
    available for convenience (e.g. Enable-PIMRole, pim).

    DEPENDENCIES

    The module requires:
      - Microsoft.Graph.Authentication (>= 2.36.0) for all Graph API calls
      - AzAuth (>= 2.9.0) for the Azure sign-in
      - PowerShell 7.4 or later, which AzAuth requires

    No Az PowerShell module is needed or loaded: the module sends every Azure
    Resource Manager request itself, and an Az session you started is left
    alone.

    No other Microsoft.Graph.* SDK modules are needed; all Graph calls use
    Invoke-MgGraphRequest directly.

    CONNECTION AND AUTHENTICATION

    All Get-/Enable-/Disable-OPIM* cmdlets authenticate automatically on first use via a
    MSAL-based browser prompt (system browser, no WAM); the Azure role cmdlets also sign in to
    Azure Resource Manager through AzAuth. Call Connect-OPIM to pre-authenticate or to target a
    specific tenant before running other cmdlets. The tokens are cached for the session --
    subsequent calls are idempotent. Disconnect-OPIM clears the module's tokens, the Azure
    Resource Manager token included, and disconnects the Microsoft Graph session; AzAuth keeps
    its own sign-in in the PowerShell process until the module's next Azure sign-in rebuilds it
    or the process ends.

      Connect-OPIM # home tenant at the first sign-in
      Connect-OPIM -TenantId 'contoso.onmicrosoft.com' # specific tenant
      Connect-OPIM -IncludeARM # also sign in to Azure
      Disconnect-OPIM # clear the module's tokens

    A session stays on the tenant it signed in to: a command that names no tenant keeps
    it, and a token issued for another tenant is refused with TenantMismatch. To work in
    another tenant, run Connect-OPIM -TenantId with it. If another Connect-MgGraph in the
    same process replaces the module's Microsoft Graph session, the role, group and sign-in
    commands refuse their calls with GraphSessionChanged instead of switching the session
    back: run Disconnect-OPIM, which also disconnects that session, and sign in again. The
    configuration commands do not sign in, and take a tenant only from the module's own
    sign-in, never from another Connect-MgGraph: Install-OPIMConfiguration without -TenantId
    stores the tenant the module is signed in to, and without such a sign-in refuses with
    TenantIdNotResolvable. Set-OPIMConfiguration keeps the stored tenant. A command whose
    sign-in at its start was refused sends nothing more (SignInRefused); a sign-in refused
    while a failed request is retried fails only that request. Azure signs in through AzAuth's
    Get-AzToken to the same tenant, and its token must be issued for that tenant and for the
    account of the Microsoft Graph sign-in (TenantMismatch or AccountMismatch otherwise). The
    module sends every Azure request itself with that token, so no Az context is used and no
    subscription is asked for; after a failed Azure sign-in (AzureConnectFailed) nothing is
    sent to Azure under an earlier one.
    Enable-OPIMMyRole and Disable-OPIMMyRole stop when the Microsoft Graph sign-in fails;
    when only the Azure sign-in fails they write the error and skip the Azure roles, unless
    the error preference is Stop, which ends the command.

    On a machine without a browser, sign in with a device code. The command shows a code and
    the address to open; enter the code there on any device. The message is written to the
    Information stream with the tag OPIMDeviceCode. With -IncludeARM, Azure shows a second code:
    AzAuth hands it over as a warning, and the module writes it on the Information stream
    instead, with the same tag, so it shows even where warnings are silenced. The mode is
    remembered for the session: the refresh stays silent while it can, and any later sign-in
    that needs a prompt uses a device code until Disconnect-OPIM. Enable-OPIMMyRole and
    Disable-OPIMMyRole take -DeviceCode too.

      Connect-OPIM -TenantId 'contoso.onmicrosoft.com' -DeviceCode

    A script reads both messages as they arrive by merging the Information stream into a
    pipeline that only shows them. The sign-in waits while that pipeline handles a message, so a
    pipeline that also waits for the user, calls the module or waits for the sign-in holds the
    sign-in up, and one that waits for the sign-in never returns: run the rest of the script after
    the command has returned. Capturing the output in a variable (for example, $x = Connect-OPIM
    -DeviceCode 6>&1) shows nothing until the flow ends, which can take 15 minutes.

      Connect-OPIM -DeviceCode -IncludeARM 6>&1 | ForEach-Object { Write-Host $PSItem.ToString() }

    The following Microsoft Graph scopes are requested automatically. These are listed for
    reference (least-privilege review, custom app registration scenarios):

      RoleEligibilitySchedule.ReadWrite.Directory
      RoleAssignmentSchedule.ReadWrite.Directory
      PrivilegedEligibilitySchedule.ReadWrite.AzureADGroup
      PrivilegedAssignmentSchedule.ReadWrite.AzureADGroup
      AdministrativeUnit.Read.All
      User.Read

    Azure RBAC cmdlets (Get-/Enable-/Disable-OPIMAzureRole) also require an Azure Resource
    Manager token, acquired automatically on first use or when -IncludeARM is passed to
    Connect-OPIM. Disconnect-OPIM clears it with the rest of the session.

    SOVEREIGN CLOUDS

    Connect-OPIM, Enable-OPIMMyRole (pim) and Disable-OPIMMyRole (unpim) take -Environment to sign
    in to a cloud other than the global one, and Install-OPIMConfiguration and
    Set-OPIMConfiguration take it to store the cloud of a tenant alias. The values, in any letter
    case:

      Global the worldwide cloud, the default; Microsoft 365 GCC is a Global tenant
      USGov US Government, GCC High
      USGovDoD US Government, DoD
      China operated by 21Vianet

    The cloud decides the sign-in authority (Microsoft Graph through MSAL, Azure through AzAuth),
    the Microsoft Graph host and the Azure Resource Manager host; the Microsoft Graph permissions
    requested are the same in every cloud. An unknown cloud is an error, never a fallback to Global.

    The cloud follows the tenant. Without -Environment, a command for the tenant the session is
    signed in to, or one that names no tenant, keeps the session's cloud, and a command for any
    other tenant uses Global. Naming another cloud signs in again, also for the same tenant. An
    organisation has a separate tenant, with its own ID, in each cloud. A session pinned by a
    tenant ID is held to that ID, so a switch of cloud without -TenantId asks the other cloud for
    a token for the same ID and fails: the other cloud most likely refuses that ID before any
    token exists, and TenantMismatch comes only if a token for another tenant does come back.
    This is untested live. Name the tenant of the cloud you switch to, or an alias that stores it.

      Connect-OPIM -TenantId 'contoso.onmicrosoft.us' -Environment USGov

    A tenant alias stores its cloud in the optional Environment key of its TenantMap.psd1 entry,
    written only when it is not Global. Connect-OPIM, pim and unpim with -TenantAlias sign in to
    the cloud the alias stores (Global when it stores none), unless -Environment names another.
    Set-OPIMConfiguration -Environment Global removes the stored cloud. A stored cloud the module
    does not know is an error, and nothing is signed in. A 0.6.x module ignores the key and signs
    in to the global cloud, where a sovereign tenant does not exist. It also writes the map back
    without the key of every alias whenever it changes it (Install-, Set- or
    Remove-OPIMConfiguration), as do 0.7.0-preview0001 to 0.7.0-preview0003, and this version
    then signs in to Global for those aliases.

    AzAuth has no authority parameter; Azure.Identity reads the AZURE_AUTHORITY_HOST environment
    variable. For a sovereign cloud the module sets it only around the Azure sign-in and puts it
    back afterwards, a failed sign-in included; for Global it writes nothing. The variable is
    process-wide, so another thread's Azure sign-in during that call would read it too.

    Only Global is verified live. USGov, USGovDoD and China are covered by unit tests only and are
    untested live. For China, Microsoft Learn says that globally registered apps do not replicate
    to Azure China and that you must register your own application there; the module signs in to
    Microsoft Graph with the Microsoft Graph Command Line Tools public client, so a China sign-in
    may fail until the module can take an application of the tenant's own. This is unverified.

    CONFIGURATION (TENANTMAP)

    The *-OPIMConfiguration cmdlets manage a TenantMap.psd1 file that maps
    short tenant aliases to Azure Tenant IDs and optional default role lists.
    A directory role is stored with its scope (roleDefinitionId|directoryScopeId), and
    Enable-OPIMMyRole and Disable-OPIMMyRole activate and deactivate it only at that scope; an
    entry written by 0.5.x holds the roleDefinitionId alone and means the role at '/' only.
    An entry may also store the cloud of its tenant as Environment (see SOVEREIGN CLOUDS).

      Install-OPIMConfiguration Create a new alias
      Get-OPIMConfiguration Read current configuration
      Set-OPIMConfiguration Update an existing alias
      Remove-OPIMConfiguration Delete an alias

    The TenantMap is stored at:
      $HOME/.config/Omnicit.PIM/TenantMap.psd1

    On Windows, $HOME is your user profile, so this is the same file as before,
    $env:USERPROFILE\.config\Omnicit.PIM\TenantMap.psd1. On Linux and macOS it is under your home
    folder. Every cmdlet that reads the map takes -TenantMapPath to use another file.

    NAMING A ROLE OR GROUP

    The Get-, Enable- and Disable- cmdlets for roles and groups take the display name of the role
    or group as -RoleName or -GroupName (the first positional argument), or the form that tab
    completion offers. A display name is compared exactly, without regard to letter case, and
    takes no wildcards. Tab completion offers the bare name whenever it is unique.

      Enable-OPIMDirectoryRole 'Usage Summary Reports Reader' -Justification 'Monthly report'
      Enable-OPIMEntraIDGroup 'Finance Team' -AccessType Owner
      Enable-OPIMAzureRole 'Reader' -Scope '/subscriptions/00000000-0000-0000-0000-000000000000'

    A name that matches more than one role or group is refused with the error AmbiguousName,
    which lists the candidates. Nothing is activated or deactivated, and the first match is never
    taken. For a role name, -Scope picks one on the cmdlets that have it (Enable-OPIMDirectoryRole,
    Disable-OPIMDirectoryRole, Enable-OPIMAzureRole, Disable-OPIMAzureRole and Get-OPIMAzureRole;
    on the Enable- and Disable- commands it does not combine with -Identity, on Get-OPIMAzureRole
    it does): '/' or an administrative unit (its /administrativeUnits/
    path or its display name) for a directory role, the ARM scope for an Azure role. A scope that
    ends in a slash is refused; only the root scope is written '/'. A group name means the
    membership, and -AccessType Owner names the ownership. Two groups with the same display name
    are refused as well: give the tab-completed form of the one you mean, which is also the way
    out everywhere else. An -Identity that matches more than one schedule is refused the same way,
    and so is an entry of a tenant alias that matches more than one active role or group when
    Disable-OPIMMyRole (unpim) reads it: that entry deactivates none of them.

    Activate an ownership (-AccessType Owner) only of a group that has another owner. When you
    are the group's only owner, PIM for Groups refuses to deactivate the ownership (Graph answers
    CannotDeleteLastAdminAssignment) and the ownership does not end at its end time. Adding a
    service principal as a direct owner of the group did not change this in testing.

    -Activated lists your activations only: the active, time-bound assignments. A permanent
    assignment is no activation and cannot be deactivated by you, so it is not listed.

    A name that matches nothing is written as EligibleRoleNotFound (ActiveRoleNotFound when
    deactivating). Deactivating a role that is eligible but not active says so (it is already
    deactivated, or its activation has not finished yet), and a name that is active under another
    key names the active form. These are non-terminating errors, so the command is not stopped.
    The Enable- cmdlets take several names; each is resolved on its own, and -Scope or -AccessType
    applies to every one of them.

    ENABLE-OPIMMYROLE (alias: pim)

    The all-in-one activation command. Connects to Microsoft Graph (and Azure
    if configured) and activates eligible roles for a named tenant alias:

      pim -TenantAlias contoso -Hours 4 -Justification 'Incident response'

    Use -AllEligible, -AllEligibleDirectoryRoles, -AllEligibleEntraIDGroups,
    or -AllEligibleAzureRoles to bypass the TenantMap filter and activate
    all eligible roles in the selected categories.

    DEFAULT ACTIVATION DURATION

    The default activation period is 1 hour. Override with -Hours or -Until,
    or set persistently via:
      $PSDefaultParameterValues['Enable-OPIM*:Hours'] = 4

    WHATIF / CONFIRM SUPPORT

    All Enable-* and Disable-* commands support -WhatIf and -Confirm:
      Get-OPIMDirectoryRole | Enable-OPIMDirectoryRole -WhatIf

COMMAND COHORTS

    The cohorts below are for orientation only: they group the cmdlets by the surface
    they work on. Use Get-Command -Module Omnicit.PIM to list every exported command.

    Directory roles
        Get-OPIMDirectoryRole, Enable-OPIMDirectoryRole, Disable-OPIMDirectoryRole,
        Wait-OPIMDirectoryRole

    Groups
        Get-OPIMEntraIDGroup, Enable-OPIMEntraIDGroup, Disable-OPIMEntraIDGroup

    Azure roles
        Get-OPIMAzureRole, Enable-OPIMAzureRole, Disable-OPIMAzureRole

    Sign-in and configuration
        Connect-OPIM, Disconnect-OPIM
        Install-OPIMConfiguration, Get-OPIMConfiguration, Set-OPIMConfiguration,
        Remove-OPIMConfiguration
        Enable-OPIMMyRole (alias pim) and Disable-OPIMMyRole (alias unpim) call Connect-OPIM
        first, then work from the tenant map for a -TenantAlias.

EXAMPLES
    PS C:\> Get-OPIMDirectoryRole
    Lists all eligible directory roles for the current user.

    PS C:\> Get-OPIMDirectoryRole | Enable-OPIMDirectoryRole -Hours 4 -Justification 'Incident response'
    Activates all eligible directory roles for 4 hours.

    PS C:\> Get-OPIMDirectoryRole | Where-Object { $_.roleDefinition.displayName -eq 'Global Administrator' } | Enable-OPIMDirectoryRole -Wait
    Activates the Global Administrator directory role and waits until it is fully provisioned,
    up to -TimeoutSeconds (default 300). The -Wait of Enable-OPIMEntraIDGroup and
    Enable-OPIMAzureRole, and Wait-OPIMDirectoryRole, take the same limit; a request still in
    progress at the limit is written as an ActivationWaitTimedOut error and stays submitted.

    PS C:\> Enable-OPIMDirectoryRole 'Usage Summary Reports Reader' -Justification 'Monthly report'
    Activates the directory role by its display name. A name that matches the role at more than
    one scope is refused with AmbiguousName; add -Scope '/' or -Scope with the administrative unit.

    PS C:\> Enable-OPIMEntraIDGroup 'Finance Team' -AccessType Owner
    Activates the ownership of the group. Without -AccessType a group name means the membership.

    PS C:\> Get-OPIMAzureRole -Activated | Disable-OPIMAzureRole
    Deactivates all active Azure RBAC roles.

    PS C:\> pim -TenantAlias contoso -Hours 8 -Justification 'Daily operations'
    Activates all stored roles for the 'contoso' tenant alias.

NOTE:
    Thank you to all those who contributed to this module, by writing code,
    sharing opinions, and providing feedback.

    Originally created by Justin Grote (@justinwgrote). Overhauled and
    maintained by Omnicit.

TROUBLESHOOTING NOTE:
    Look out on the Github repository for issues and new releases.

    RoleAssignmentRequestAcrsValidationFailed / ACRS claims challenge
    -----------------------------------------------------------------
    If an Enable-OPIM* command fails with the error code
    'RoleAssignmentRequestAcrsValidationFailed', the Microsoft Graph token does
    not satisfy the step-up authentication requirement (ACRS claim 'c1') enforced
    by a Conditional Access policy for PIM operations.

    The module automatically attempts to acquire a new token with the required
    claims via MSAL interactive authentication (opens a browser window). If
    this succeeds the request is retried transparently.

    If the automatic recovery fails, the error message directs you to open a
    new PowerShell session and reconnect:

        # In a fresh PowerShell window:
        Connect-OPIM
        Enable-OPIMDirectoryRole -RoleName '...'

    If the issue persists, the tenant may require a custom app registration
    for claims-challenge support.

    Bearer token security
    ---------------------
    When a Microsoft Graph request or an Azure role command fails, the module
    clears the Authorization header, which carries the bearer token, on the
    request the error record points at, and removes the raw record from $Error,
    the moment it catches the failure. A Graph or an Azure Resource Manager error
    reaches you as a new record that does not chain the original exception.

SEE ALSO
    - https://github.com/Omnicit/Omnicit.PIM

KEYWORDS
    PIM, Privileged Identity Management, Entra ID, Azure AD, RBAC, Graph,
    Directory Roles, Azure Roles, PIM Groups, Self-Activation