Data/GuardrailMap.psd1

@{
    # ------------------------------------------------------------------------
    # PSAutoRBAC guardrail (gate / block) knowledge base
    # ------------------------------------------------------------------------
    # Having the required RBAC role is necessary but NOT sufficient: a caller can
    # hold Contributor and still be blocked from creating a resource by an Azure
    # Policy 'deny', and an executor can hold roleAssignments/write and still be
    # blocked from granting a role by a deny assignment, a Conditional Access
    # policy, or a role that is only PIM-eligible (not active).
    #
    # This file enumerates the *guardrails* - preventive, identity, network, and
    # detective controls - that can prohibit either the action itself or the
    # setting of the role, per platform. Providers read it as their offline
    # advisory source; the Azure and Graph providers additionally detect several
    # of these live when the relevant module is present and -Live is requested.
    #
    # Schema per guard entry:
    # Affects = 'Action' | 'RoleAssignment' | 'Both'
    # Does the control block the operation, the role grant, or both?
    # Category = 'Preventive' | 'Identity' | 'Network' | 'Detective'
    # Severity = 'High' | 'Medium' | 'Info'
    # Detail = what the control is and how it blocks
    # Mitigation = how to detect / unblock (least-privilege, reversible)
    # Reference = Microsoft Learn URL
    # LiveCheck = the cmdlet used for live detection, or 'Advisory' (offline only)
    #
    # Status is decided at evaluation time: advisory entries surface as
    # 'Potential', live detection promotes them to 'Blocking' or 'Clear', and
    # detective controls surface as 'Detective'.
    # ------------------------------------------------------------------------

    'Azure' = @{

        'AzurePolicyDeny' = @{
            Affects    = 'Both'
            Category   = 'Preventive'
            Severity   = 'High'
            Detail     = "An Azure Policy assignment with a 'deny' effect at or above the target scope rejects the request at the ARM layer AFTER RBAC authorizes it - e.g. denied locations, required tags, disallowed SKUs, or 'deny Microsoft.Authorization/roleAssignments' which blocks the grant itself."
            Mitigation = "List deny-effect assignments in scope (Get-AzPolicyAssignment) and evaluate Get-AzPolicyState for the resource type. Bring the request into compliance, or request a policy exemption (Microsoft.Authorization/policyExemptions/write) rather than removing the policy."
            Reference  = 'https://learn.microsoft.com/azure/governance/policy/concepts/effect-deny'
            LiveCheck  = 'Get-AzPolicyAssignment'
        }

        'DenyAssignment' = @{
            Affects    = 'Both'
            Category   = 'Preventive'
            Severity   = 'High'
            Detail     = 'A deny assignment explicitly blocks specified actions for specified principals at a scope, and deny assignments take precedence over role assignments. They are created by Azure (Blueprints, managed applications, Deployment Stacks deny settings) - not directly by users - so a caller with the role can still be denied the action or the roleAssignments/write.'
            Mitigation = 'List deny assignments at scope (Get-AzDenyAssignment) and identify the owning system (managed app, Deployment Stack denySettings, Blueprint). Adjust the source (e.g. Deployment Stack deny settings excluded principals) rather than trying to override the deny.'
            Reference  = 'https://learn.microsoft.com/azure/role-based-access-control/deny-assignments'
            LiveCheck  = 'Get-AzDenyAssignment'
        }

        'ResourceLock' = @{
            Affects    = 'Action'
            Category   = 'Preventive'
            Severity   = 'Medium'
            Detail     = "A management lock overrides RBAC: 'CanNotDelete' blocks delete operations and 'ReadOnly' blocks all write/delete on the resource, resource group, or subscription - regardless of the caller's role."
            Mitigation = "List locks at scope (Get-AzResourceLock). Removing a lock needs Microsoft.Authorization/locks/delete (Owner or User Access Administrator). Prefer removing the lock only for the operation window, then re-applying it."
            Reference  = 'https://learn.microsoft.com/azure/azure-resource-manager/management/lock-resources'
            LiveCheck  = 'Get-AzResourceLock'
        }

        'RoleAssignmentCondition' = @{
            Affects    = 'RoleAssignment'
            Category   = 'Preventive'
            Severity   = 'Medium'
            Detail     = 'An ABAC condition on the executor role assignment (e.g. a Role Based Access Control Administrator constrained by conditions) can restrict WHICH roles or principals the executor may assign, so the grant can fail even though roleAssignments/write is held.'
            Mitigation = "Inspect the executor's own assignment for a Condition (Get-AzRoleAssignment | Where Condition). Grant a role permitted by the condition, or have an unconstrained access administrator perform the grant."
            Reference  = 'https://learn.microsoft.com/azure/role-based-access-control/conditions-overview'
            LiveCheck  = 'Advisory'
        }

        'PrivilegedIdentityManagement' = @{
            Affects    = 'Both'
            Category   = 'Identity'
            Severity   = 'Medium'
            Detail     = 'The required Azure resource role may be assigned as PIM-eligible rather than active. Until the principal activates it (and satisfies any MFA / approval / justification / ticket requirement), the role is not effective and both the action and any role grant it authorizes will be denied.'
            Mitigation = 'Check for an eligible-but-inactive schedule (Get-AzRoleEligibilitySchedule / Get-AzRoleAssignmentSchedule) and activate the role for the operation window, or grant a time-bound active assignment.'
            Reference  = 'https://learn.microsoft.com/entra/id-governance/privileged-identity-management/pim-resource-roles-activate-your-roles'
            LiveCheck  = 'Get-AzRoleEligibilitySchedule'
        }

        'DefenderForCloudEnforcement' = @{
            Affects    = 'Action'
            Category   = 'Preventive'
            Severity   = 'Medium'
            Detail     = "Microsoft Defender for Cloud ships regulatory / secure-score initiatives as Azure Policy. In 'Enforce' mode a recommendation is backed by a deny or deployIfNotExists policy (e.g. require secure transfer, deny public blob access), so a non-compliant deployment is blocked or mutated."
            Mitigation = 'Review the Defender for Cloud regulatory-compliance initiative assignments (they surface as policy assignments). Bring the resource into compliance with the security baseline, or exempt the specific control.'
            Reference  = 'https://learn.microsoft.com/azure/defender-for-cloud/concept-regulatory-compliance'
            LiveCheck  = 'Advisory'
        }

        'MicrosoftSentinel' = @{
            Affects    = 'Action'
            Category   = 'Detective'
            Severity   = 'Info'
            Detail     = 'Microsoft Sentinel does not block actions, but analytics rules and SOAR automation can detect and respond to privileged operations (role grants, resource creation). A grant may trigger an alert, an automation rule, or an approval workflow - i.e. the operation is watched even where it is not prevented.'
            Mitigation = 'Expect the operation to be logged. Coordinate privileged changes with the SecOps runbook, use an approved change window, and ensure justification is recorded so automation does not flag it as anomalous.'
            Reference  = 'https://learn.microsoft.com/azure/sentinel/automation/automation'
            LiveCheck  = 'Advisory'
        }

        'AzureFirewall' = @{
            Affects    = 'Action'
            Category   = 'Network'
            Severity   = 'Medium'
            Detail     = 'A data-plane operation (blob upload, Key Vault secret read, SQL query) can be blocked in the network path by Azure Firewall / a secured virtual hub even when RBAC allows it - the request never reaches the resource endpoint.'
            Mitigation = 'Confirm the caller egress path allows the resource FQDN/service tag (Azure Firewall application/network rules). Run from an allowed subnet or add an allow rule for the operation.'
            Reference  = 'https://learn.microsoft.com/azure/firewall/protect-azure-storage'
            LiveCheck  = 'Advisory'
        }

        'NetworkSecurityGroup' = @{
            Affects    = 'Action'
            Category   = 'Network'
            Severity   = 'Medium'
            Detail     = 'A Network Security Group on the caller subnet/NIC can deny outbound access to a resource or service tag (e.g. Storage, AzureKeyVault, AzureResourceManager), blocking a data-plane operation regardless of RBAC.'
            Mitigation = 'Inspect the effective outbound NSG rules (Get-AzEffectiveNetworkSecurityGroup) for a deny to the target service tag, and add a scoped allow rule for the operation.'
            Reference  = 'https://learn.microsoft.com/azure/virtual-network/network-security-groups-overview'
            LiveCheck  = 'Advisory'
        }

        'ResourceFirewall' = @{
            Affects    = 'Action'
            Category   = 'Network'
            Severity   = 'Medium'
            Detail     = "A resource-level firewall can block a data-plane call even with the correct data role: a storage account or Key Vault with 'Public network access = Disabled' or default-deny networkAcls only admits selected VNets / IPs / private endpoints. RBAC passes; the network layer rejects the call."
            Mitigation = "Check publicNetworkAccess and networkAcls on the resource (Get-AzStorageAccount / Get-AzKeyVault). Run from an allowed network / private endpoint, or add the caller IP to the resource firewall for the operation window."
            Reference  = 'https://learn.microsoft.com/azure/storage/common/storage-network-security'
            LiveCheck  = 'Advisory'
        }
    }

    'Microsoft Graph' = @{

        'ConditionalAccess' = @{
            Affects    = 'Both'
            Category   = 'Identity'
            Severity   = 'High'
            Detail     = 'An Entra Conditional Access policy is evaluated at sign-in / token issuance and can block the caller outright or impose grant controls (MFA, compliant / hetero-joined device, approved app, named location). Policies targeting privileged roles or the target app can stop both the action and a role grant before any RBAC check runs.'
            Mitigation = 'Review Conditional Access policies that target the principal, the app, or privileged roles (Get-MgIdentityConditionalAccessPolicy). Satisfy the grant controls (MFA / compliant device / allowed location), or scope an exclusion for the workload identity through the policy owner.'
            Reference  = 'https://learn.microsoft.com/entra/identity/conditional-access/overview'
            LiveCheck  = 'Get-MgIdentityConditionalAccessPolicy'
        }

        'PrivilegedIdentityManagement' = @{
            Affects    = 'Both'
            Category   = 'Identity'
            Severity   = 'High'
            Detail     = 'The required Entra directory role (e.g. Privileged Role Administrator to grant a role, or Groups Administrator to act) may be PIM-eligible rather than active. Until activated - satisfying MFA, justification, and any approval - the directory role is inert and the operation is denied.'
            Mitigation = 'Check for eligible-but-inactive directory-role schedules (Get-MgRoleManagementDirectoryRoleEligibilitySchedule) and activate the role for the change window, honouring the activation requirements.'
            Reference  = 'https://learn.microsoft.com/entra/id-governance/privileged-identity-management/pim-how-to-activate-role'
            LiveCheck  = 'Get-MgRoleManagementDirectoryRoleEligibilitySchedule'
        }

        'RestrictedManagementAdminUnit' = @{
            Affects    = 'RoleAssignment'
            Category   = 'Identity'
            Severity   = 'Medium'
            Detail     = 'If the target principal (or group) is in a restricted-management administrative unit, tenant-level role holders cannot modify it. Only administrators scoped to that admin unit can assign roles to, or manage, the protected object - so an otherwise-privileged executor is blocked.'
            Mitigation = "Determine whether the principal is in a restricted-management AU (Get-MgDirectoryAdministrativeUnit) and perform the grant as an administrator scoped to that AU."
            Reference  = 'https://learn.microsoft.com/entra/identity/role-based-access-control/admin-units-restricted-management'
            LiveCheck  = 'Advisory'
        }

        'AdminConsentRequired' = @{
            Affects    = 'Action'
            Category   = 'Identity'
            Severity   = 'Medium'
            Detail     = 'A delegated Graph permission the command needs may not be consented for the caller, and the tenant consent policy may forbid user self-consent. Without prior admin consent the token lacks the scope and the call fails with insufficient privileges - independent of directory-role membership.'
            Mitigation = 'Confirm the required delegated scope / app role is admin-consented for the app (review the app registration API permissions and grant admin consent), then reconnect to obtain a token carrying the scope.'
            Reference  = 'https://learn.microsoft.com/entra/identity/enterprise-apps/user-admin-consent-overview'
            LiveCheck  = 'Advisory'
        }

        'AuthenticationStrength' = @{
            Affects    = 'Action'
            Category   = 'Identity'
            Severity   = 'Medium'
            Detail     = 'Privileged operations may require an authentication-strength / phishing-resistant MFA method the current session does not satisfy (often paired with Conditional Access or PIM activation). The token is issued but rejected for the privileged action.'
            Mitigation = 'Ensure the session meets the required authentication strength (e.g. FIDO2 / certificate-based) before the privileged operation, and re-authenticate if the current method is insufficient.'
            Reference  = 'https://learn.microsoft.com/entra/identity/authentication/concept-authentication-strengths'
            LiveCheck  = 'Advisory'
        }

        'DefenderForIdentity' = @{
            Affects    = 'Action'
            Category   = 'Detective'
            Severity   = 'Info'
            Detail     = 'Microsoft Defender for Identity / Entra ID Protection monitor privileged identity activity and can raise risk detections on anomalous role grants or sign-ins. A risk detection can, in turn, trigger a Conditional Access risk policy that blocks the session.'
            Mitigation = 'Perform privileged grants from a known, low-risk session and device so identity protection does not raise the user/sign-in risk that a risk-based Conditional Access policy would then block.'
            Reference  = 'https://learn.microsoft.com/defender-for-identity/what-is'
            LiveCheck  = 'Advisory'
        }
    }

    'Microsoft Fabric' = @{

        'TenantSetting' = @{
            Affects    = 'Action'
            Category   = 'Preventive'
            Severity   = 'High'
            Detail     = "Fabric admin tenant settings gate capabilities independent of workspace roles: 'Create workspaces', 'Service principals can use Fabric APIs', 'Users can create Fabric items', and export/sharing switches. If the relevant switch excludes the principal (or its security group), the operation is blocked even for a workspace Admin."
            Mitigation = 'Have a Fabric administrator confirm the relevant tenant setting is enabled for the principal (or a group it belongs to) in the Admin portal / via the admin APIs, scoping the switch to a security group rather than the whole tenant.'
            Reference  = 'https://learn.microsoft.com/fabric/admin/about-tenant-settings'
            LiveCheck  = 'Advisory'
        }

        'CapacityState' = @{
            Affects    = 'Action'
            Category   = 'Preventive'
            Severity   = 'Medium'
            Detail     = 'A workspace assigned to a paused, deleted, or over-consumed (throttled) Fabric / Power BI capacity cannot run operations. The workspace role is intact but the compute the operation needs is unavailable, so the request fails or is throttled.'
            Mitigation = 'Verify the workspace capacity is active and within its consumption limits (resume a paused capacity, or move the workspace to a healthy capacity) before retrying.'
            Reference  = 'https://learn.microsoft.com/fabric/enterprise/throttling'
            LiveCheck  = 'Advisory'
        }

        'ConditionalAccess' = @{
            Affects    = 'Both'
            Category   = 'Identity'
            Severity   = 'Medium'
            Detail     = 'Fabric / Power BI is an Entra app, so a Conditional Access policy targeting it can block the caller or require grant controls (MFA, compliant device, allowed location) before any workspace-role check applies.'
            Mitigation = 'Review Conditional Access policies targeting the Power BI / Fabric service and satisfy the grant controls, or arrange a scoped exclusion for the automation identity.'
            Reference  = 'https://learn.microsoft.com/power-bi/enterprise/service-admin-conditional-access'
            LiveCheck  = 'Advisory'
        }
    }

    'Microsoft Purview' = @{

        'MetadataPolicyGate' = @{
            Affects    = 'RoleAssignment'
            Category   = 'Preventive'
            Severity   = 'Medium'
            Detail     = 'Purview data-plane roles are granted by editing a collection metadata policy, and only a Collection Admin on that collection (or an ancestor, when permissions are inherited) may edit it. An executor who is not a Collection Admin cannot set the role even with other privileges.'
            Mitigation = 'Confirm the executor is a Collection Admin on the target collection (or an ancestor with inheritance enabled) before attempting a metadata-policy edit; otherwise delegate the grant to a Collection Admin.'
            Reference  = 'https://learn.microsoft.com/purview/how-to-create-and-manage-collections'
            LiveCheck  = 'Advisory'
        }

        'CollectionInheritance' = @{
            Affects    = 'Action'
            Category   = 'Preventive'
            Severity   = 'Info'
            Detail     = 'Access can be broken by collection hierarchy: a child collection may restrict permission inheritance from its parent, so a role held at the root does not flow down. The role appears held higher up yet is not effective at the child collection.'
            Mitigation = 'Verify the target collection inherits permissions (or assign the role directly on the child collection) so the required role is effective at the operation scope.'
            Reference  = 'https://learn.microsoft.com/purview/reference-purview-role-catalog'
            LiveCheck  = 'Advisory'
        }

        'PurviewNetworkFirewall' = @{
            Affects    = 'Action'
            Category   = 'Network'
            Severity   = 'Medium'
            Detail     = "A Purview account with public network access disabled (private-endpoint only) rejects data-plane calls that do not arrive over an approved private endpoint, regardless of the caller's metadata-policy role."
            Mitigation = 'Run the operation from a network with an approved private endpoint to the Purview account, or temporarily allow the caller network on the account for the operation window.'
            Reference  = 'https://learn.microsoft.com/purview/catalog-private-link'
            LiveCheck  = 'Advisory'
        }
    }
}