Omnicit.EntraRBAC.psd1

@{
    RootModule           = 'Omnicit.EntraRBAC.psm1'
    ModuleVersion        = '1.1.0'
    CompatiblePSEditions = @('Core')
    GUID                 = '7b9e4a1c-2d6f-4f3a-9c8b-1e5d0a7c3f42'
    Author               = 'Omnicit AB / Philip Haglund'
    CompanyName          = 'Omnicit'
    Copyright            = '(c) 2026 Omnicit AB'
    Description          = 'Manage Entra ID and Azure RBAC building blocks across tenants: Entra ID groups, PIM, Administrative Units, Entitlement Management, Access Reviews, Azure resources and RBAC, plus a JSON inventory and declarative apply engine.'
    PowerShellVersion    = '7.2'

    RequiredModules = @(
        @{ ModuleName = 'AzAuth'; ModuleVersion = '2.9.0' }
        @{ ModuleName = 'Microsoft.Graph.Authentication'; ModuleVersion = '2.36.0' }
    )

    # Loaded via Update-TypeData in suffix.ps1 (Remove-Module does not clean type data).
    TypesToProcess   = @()
    FormatsToProcess = @('Formats/Omnicit.EntraRBAC.Format.ps1xml')

    FunctionsToExport = @('Add-OERAccessPackageResourceRole','Add-OERAdministrativeUnitMember','Add-OERAdministrativeUnitScopedRole','Add-OERCatalogResource','Add-OERGroupEligibility','Add-OERGroupMember','Connect-OER','Disable-OEREligibleRoleAssignment','Disconnect-OER','Enable-OEREligibleRoleAssignment','Export-OERInventory','Get-OERAccessPackage','Get-OERAccessPackageAssignment','Get-OERAccessPackageAssignmentPolicy','Get-OERAccessPackageResourceRole','Get-OERAccessReviewDefinition','Get-OERAccessReviewInstance','Get-OERAccessReviewInstanceDecision','Get-OERActiveDirectoryRoleAssignment','Get-OERActiveRoleAssignment','Get-OERAdministrativeUnit','Get-OERAdministrativeUnitScopedRole','Get-OERAuthenticationContext','Get-OERCatalog','Get-OERCatalogResource','Get-OERConfiguration','Get-OERDirectoryRoleManagementPolicy','Get-OEREligibleDirectoryRoleAssignment','Get-OEREligibleRoleAssignment','Get-OERGroup','Get-OERGroupEligibility','Get-OERGroupMember','Get-OERGroupPimPolicy','Get-OERInventory','Get-OERManagementGroup','Get-OERRequiredScope','Get-OERResource','Get-OERResourceGroup','Get-OERRoleAssignment','Get-OERRoleDefinition','Get-OERRoleManagementPolicy','Get-OERSubscription','Invoke-OERAccessReviewInstanceDecision','Invoke-OERStructure','New-OERAccessPackage','New-OERAccessPackageApprovalStage','New-OERAccessPackageAssignment','New-OERAccessPackageAssignmentPolicy','New-OERAccessPackageRequestorScope','New-OERAccessPackageRequestorSettings','New-OERAccessReviewDefinition','New-OERAccessReviewStage','New-OERActiveDirectoryRoleAssignment','New-OERActiveRoleAssignment','New-OERAdministrativeUnit','New-OERCatalog','New-OERConfiguration','New-OEREligibleDirectoryRoleAssignment','New-OEREligibleRoleAssignment','New-OERGroup','New-OERPolicyNotificationRule','New-OERResourceGroup','New-OERRoleAssignment','Remove-OERAccessPackage','Remove-OERAccessPackageAssignment','Remove-OERAccessPackageAssignmentPolicy','Remove-OERAccessPackageResourceRole','Remove-OERAccessReviewDefinition','Remove-OERActiveDirectoryRoleAssignment','Remove-OERActiveRoleAssignment','Remove-OERAdministrativeUnit','Remove-OERAdministrativeUnitMember','Remove-OERAdministrativeUnitScopedRole','Remove-OERCatalog','Remove-OERCatalogResource','Remove-OERConfiguration','Remove-OEREligibleDirectoryRoleAssignment','Remove-OEREligibleRoleAssignment','Remove-OERGroup','Remove-OERGroupEligibility','Remove-OERGroupMember','Remove-OERResourceGroup','Remove-OERRoleAssignment','Send-OERAccessReviewReminder','Set-OERAccessPackage','Set-OERAccessPackageAssignmentPolicy','Set-OERAccessReviewDefinition','Set-OERAdministrativeUnit','Set-OERCatalog','Set-OERConfiguration','Set-OERDirectoryRoleManagementPolicy','Set-OERGroup','Set-OERGroupPimPolicy','Set-OERResourceGroup','Set-OERRoleAssignment','Set-OERRoleManagementPolicy','Stop-OERAccessReviewInstance','Test-OERStructure')
    CmdletsToExport   = @()
    VariablesToExport = @()
    AliasesToExport   = @()

    PrivateData = @{
        PSData = @{
            Tags                     = @('EntraID', 'Azure', 'RBAC', 'PIM', 'Identity', 'Governance',
                                          'PSEdition_Core', 'Windows', 'Linux', 'MacOS')
            ProjectUri               = 'https://github.com/Omnicit/Omnicit.EntraRBAC'
            LicenseUri               = 'https://github.com/Omnicit/Omnicit.EntraRBAC/blob/main/LICENSE'
            RequireLicenseAcceptance = $false
            ReleaseNotes             = '## [1.1.0] - 2026-10-01

`Invoke-OERStructure -Prune` no longer removes anything because a lookup failed: a declared entry
that cannot be resolved is `Failed`, and the undeclared live entries of its collection are `Skipped`
with `prune withheld`; earlier versions could delete them. An ambiguous service principal name is
refused with the candidate ids instead of taking the first match: `AmbiguousApplicationName` for a
catalog or access package resource, `AmbiguousPrincipalName` for a principal (an ambiguous group,
formerly `PrincipalNotFound`, too). A service principal in `roleAssignments` needs
`"principalType": "ServicePrincipal"`. `Test-OERStructure` and `-Prune` warn about an omitted
`members`, `scopedRoles`, `resources` or `resourceRoles` key, which still prunes; set such a key to
`null` to leave it alone.

PIM for Groups policies support approval: `Set-OERGroupPimPolicy -RequireApproval`, `-ApproverUser`
and `-ApproverGroup`, and `requireApproval` and `approvers { users[], groups[] }` in `pimPolicy`.
Approver names are resolved before comparison; a `roleManagementPolicies` user approver must be a
UPN or object id. Earlier versions could apply a group''s owner settings, permanent eligibility
included, to its member policy: review the member policies of groups an apply run onboarded. A
refused policy read is `PimPolicyReadFailed`, not `PimPolicyNotFound`. Exports carry `pimPolicy`
only for a group with PIM eligibility or a modified policy, and `Invoke-OERStructure` warns before a
`pimPolicy` change onboards an existing group, which cannot be undone.

`Get-` and `Set-OERDirectoryRoleManagementPolicy` manage a directory role''s PIM settings; approvers
are set per side, so `-ApproverUser` keeps the group approvers and an empty list clears a side.
`New-`, `Get-` and `Remove-OEREligibleDirectoryRoleAssignment` and their active counterparts assign
roles, and the apply sections `directoryRoleManagementPolicies[]` and `directoryRoleAssignments[]`
run before the Azure sections. A permanent assignment the role''s policy forbids is refused instead
of opening the policy, as is a group that is not role-assignable. `-Prune` touches only the declared
role and assignment-type pairs, never an activation, an inherited assignment, or a direct assignment
of the signed-in identity or its groups. Graph refuses changes to a principal''s role assignments for
five minutes after an active one starts (`Failed`).

`Export-OERInventory` includes both directory role sections by default, as
`directoryRoleManagementPolicies.json` and `directoryRoleAssignments.json`. Policies are exported
for roles with an assignment, or for every role with `-AllDirectoryRolePolicies`. Only direct,
tenant-scope assignments are exported, never activations. Both re-apply to the same tenant as
`Unchanged`; a failed read is `InventoryPartial`, never an empty section. With an Azure section
included, `azurePimEligibility.json` lists the walked scopes'' Azure PIM eligible assignments as
read-only context; unread scopes, a refused management-group listing included, are named in
`SkippedEligibilityScopes`. Every per-area file is now a JSON array, `[]` when empty.

Groups can be renamed with `Set-OERGroup -NewDisplayName`, or with `previousDisplayName` (the
current name or object id) beside the new `displayName`. If both names match different groups the
entry fails with `GroupRenameConflict`, and if neither matches, as while Graph''s name lookup lags a
rename, with `GroupRenameNotFound`; nothing is merged or created. A catalog''s Group or Application
resource is matched by the object id its name resolves to, not by the name the catalog recorded,
which outlives a rename; exports write the current name. `Set-OERGroup` on a role-assignable group
needs `RoleManagement.ReadWrite.Directory`.

'

            Prerelease               = ''
        }
    }
}