SfSkills Domains Docs GitHub

Home / admin / permission-set-architecture

permission-set-architecture

Use when designing or refactoring Salesforce access architecture around minimal profiles, permission sets, permission set groups, muting, and assignment governance.

Open the skill folder on GitHub


Use this skill when the problem is no longer "which permission do I grant" and has become "how should the org structure access so future changes stay safe?" The goal is to produce a layered access model that supports least privilege, lowers operational risk, and makes persona-based changes predictable.

Before Starting

Questions to Ask Before Configuring

Answer these before the first .permissionset-meta.xml is written. Each one maps to a platform behaviour documented in references/gotchas.md; skipping them produces a bundle that deploys cleanly and grants the wrong thing.

AskWhy it mattersWhat a good answer adds
"Which user or permission set license does every person in this persona hold?"The license element pins a permission set to one license; a mixed-license persona needs it left emptyOne bundle family per license boundary, or a deliberate empty license recorded as a choice
"Which objects does this capability touch, and at what CRUD level?"Object permissions have a dependency chain (allowEdit needs read, allowDelete needs read + edit), and field permissions cannot exceed the object grantThe object-access slice, sized before any FLS is written
"Does anything here need View All Records or Modify All Records?"Those bypass sharing per object; modifyAllRecords additionally requires read, edit, delete, and viewAllRecordsA decision to isolate the override in its own set that is never composed into a persona
"Is this bundle for a named person or for a standing role that changes people?"The PSG is the reviewable assignment unit; a per-person pile of sets has no name to attest againstThe persona → PSG map, plus the owner of each PSG
"Should any of this expire?"PermissionSetAssignment.ExpirationDate (API 52.0+) time-boxes elevation instead of leaving it to driftA time-limited set handed to admin/permission-set-expiration rather than a permanent grant
"Does any permission need step-up activation?"hasActivationRequired survives standalone assignment but not group membershipSession-based sets kept out of persona PSGs on purpose
"Who checks the group after deploy, and against which user?"PSG recalculation is a state machine, not a synchronous write; effective access lags the metadataA post-deploy PermissionSetGroup.Status poll and a real-user verification step

What a proper configuration adds over just doing it: every grant traces to one capability set, the license boundary is a design input instead of a deploy error, sharing-bypass permissions are visible in one place, and the persona bundle can be re-attested a year later without re-deriving it from user records.

Core Concepts

Profiles Are The Base Layer, Not The Whole Architecture

Profiles still matter for baseline settings such as login hours, page layout assignment, and a minimal set of app access, but they should not carry every feature permission for every job role. When profiles become job-title snapshots, every change request turns into profile cloning, regression risk, and audit noise.

Note that ObjectPermissions and FieldPermissions records are children of PermissionSet, not of Profile — the Object Reference states this for both objects. Every profile is itself backed by a permission set (PermissionSet.IsOwnedByProfile = true), which is why a profile-vs-permission-set audit can be written as one SOQL query over PermissionSet.

Permission Sets Should Model Capabilities

Permission sets work best when they describe a coherent capability such as "Case Console Core", "Refund Approval", or "Quote Generation". They become difficult to govern when they are named after people, projects, or emergency exceptions. Capability-based design is what makes bundle reuse possible.

The canonical slicing taxonomy — App / Object / Feature / Setup / Session-Based / Time-Limited, with naming patterns for each — lives in templates/admin/permission-set-patterns.md. Map every requested entitlement to exactly one of those categories before writing XML; do not re-derive a taxonomy per project.

Permission Set Groups Are The Assignment Unit

A user should rarely receive a large pile of unrelated permission sets one by one. Permission Set Groups let the architecture describe a persona or bundle as a unit. That makes onboarding, change review, and attestation easier because the assignment object has meaning.

PermissionSetAssignment carries either PermissionSetId or PermissionSetGroupId (the group field is API 45.0+), so the same assignment object serves both — a governance report can count how many assignments are to a named persona group versus loose sets.

Muting And Exceptions Need Governance

Muting is useful when a shared bundle is almost right but slightly too broad. It is not a substitute for coherent bundle design. A good architecture limits subtractive exceptions and documents who owns them, because muted access can drift away from the intent of the base bundle if nobody reviews it.

A MutingPermissionSet has the same fields as PermissionSet plus label, but the Metadata API guide inverts the meaning: settings enabled in a muting permission set are turned off for the group it belongs to. A muting set that lists a permission no member set grants is dead weight, and reads to a future admin as if it were granting something.

The Deployable Shapes

Typepackage.xml <name>File in a DX projectAPI
Permission setPermissionSet (wildcard * allowed)permissionsets/Obj_Case_Full.permissionset-meta.xml22.0+
Permission set groupPermissionSetGroup (wildcard * allowed)permissionsetgroups/Persona_Service_Agent.permissionsetgroup-meta.xml45.0+
Muting permission setMutingPermissionSetmutingpermissionsets/Mute_Tier1_Refunds.mutingpermissionset-meta.xml46.0+

Deployable examples of all three, plus package.xml, retrieve/deploy commands, assignment, and verification SOQL, are in references/metadata-examples.md.

Common Patterns

Baseline Plus Feature Bundles

When to use: Multiple business personas share a common baseline but need different feature combinations.

How it works: Keep profiles thin, create focused permission sets per capability, then compose job-specific bundles through permission set groups. Use naming that exposes scope and ownership.

Why not the alternative: One profile per persona mixes baseline setup and feature entitlements into one brittle object. Every new feature becomes a profile-change project.

Shared Bundle With Controlled Muting

When to use: Several personas need the same large bundle except for a few permissions that one persona must not receive.

How it works: Build one reusable PSG, apply muting only for the delta, and track the business reason for the muted variant.

Why not the alternative: Cloning nearly identical groups or permission sets multiplies maintenance and makes audits harder.

Migration Off Profile-Centric Access

When to use: The org already has many custom profiles and access changes are slow or unsafe.

How it works: Identify common permissions in profiles, move feature access to permission sets, introduce PSGs for recurring personas, and leave profiles as thinner baselines over time.

Decision Guidance

SituationRecommended ApproachReason
One capability must be granted to many personasPermission SetCapability-level reuse is cleaner than editing many profiles
A recurring job role needs several capability bundles togetherPermission Set GroupPSGs are the right assignment and review unit
One persona needs slightly less than the shared bundleMuting Permission Set in a PSGSubtractive exception is clearer than cloning the whole bundle
The persona spans Salesforce and Platform licensesTwo license-scoped set families, license left empty on shared setslicense pins a set to one license and blocks assignment to the rest
A capability needs step-up authentication before useStandalone session-based set, assigned directlyGroup membership removes the session-activation requirement
Users see the right permissions but the wrong recordsUse sharing-model skills insteadPermission architecture does not solve record visibility
A request is truly one-off and temporaryTime-boxed permission set assignmentAvoid distorting the core architecture for an exception
  1. Slice the entitlement list — map every requested permission to exactly one category in templates/admin/permission-set-patterns.md (App / Object / Feature / Setup / Session-Based / Time-Limited). One concern per permission set; a set that needs "and" in its name is two sets.
  2. Draw the license boundaries — group the slices into families per user or permission set license, and decide per set whether license is populated or deliberately empty. A shared set assigned across Salesforce and Platform users must leave it empty.
  3. Write object access before field access — objectPermissions first, honouring the dependency chain, then fieldPermissions only for fields the object grant can carry. Shapes and the full element list are in references/metadata-examples.md.
  4. Compose the personas — one PSG per persona listing its permissionSets; add a mutingPermissionSets entry only for a delta that splitting a set cannot express, and record the business reason in the muting set's description.
  5. Check the tree — python3 skills/admin/permission-set-architecture/scripts/check_permission_set_architecture.py --manifest-dir force-app/main/default. It flags edit-without-read FLS, sharing-bypass grants, oversized sets, dangling PSG members, session sets inside groups, no-op muting entries, and an over-length description (PSA-DESC-01 ERROR at 255+ characters, PSA-DESC-02 INFO headroom at 200+, never fails the run).
  6. Deploy in order and wait for recalculation — permission sets before groups before muting; then poll PermissionSetGroup.Status until it reads Updated (see references/metadata-examples.md). A Failed status is silent in the deploy result.
  7. Verify against a real user — run the PermissionSetAssignment and ObjectPermissions verification queries in references/metadata-examples.md for one member of each persona, then record the persona-to-bundle matrix in templates/permission-set-architecture-template.md.

Review Checklist

Salesforce-Specific Gotchas

  1. Permission Set Group recalculation is a state machine, not a synchronous write — Status moves through Updated, Outdated, Updating, and Failed, so a rollout plan must poll it rather than assume the deploy result is the whole story.
  2. License fit matters before architecture elegance — a permission set with license populated can only be held by users on that license, so a clean bundle model still fails if it ignores license boundaries.
  3. Muting only subtracts from grouped permissions — it never grants access and it cannot fix a poor base design that should have been split into smaller capabilities.
  4. Apex class, tab, app, and object access often drift separately — teams sometimes move object permissions into permission sets but forget Apex class access and UI entry points, creating half-migrated bundles.
  5. A session-based set loses its step-up requirement inside a group — the Object Reference is explicit that permissions in session-based sets included in a PSG no longer require session activation.
  6. A description over 255 characters fails the deploy, and PSGs fail as a cascade — the Metadata API guide caps PermissionSet.description and Profile.description at 255 characters; a permission set rejected on that limit takes every PSG that composes it down with it (references/gotchas.md).
  7. A Platform Event is an object for permission purposes — a persona bundle that publishes a __e via automation needs an objectPermissions grant for that event just like any other object, and at the API 67.0 user-mode default a missing grant fails silently at runtime, not at deploy (references/gotchas.md).

Deeper treatment, each with the platform behaviour behind it, in references/gotchas.md.

Output Artifacts

ArtifactDescription
permissionsets/*.permissionset-meta.xmlCapability-sliced sets, each mapped to one taxonomy category
permissionsetgroups/*.permissionsetgroup-meta.xmlOne per persona, listing permissionSets and any mutingPermissionSets
mutingpermissionsets/*.mutingpermissionset-meta.xmlDocumented subtractive deltas only
Access architecture reviewFindings on profile sprawl, bundle reuse, muting overuse, and governance gaps
Persona-to-bundle matrixRecommended baseline profile, permission sets, and PSGs per persona
Migration backlogSequenced changes for moving from profile-heavy access to governed bundles

Reference Files

FileRead it when
references/metadata-examples.mdWriting the XML: object set, function set, persona PSG, muting set, package.xml, retrieve/deploy, assignment, verification SOQL
references/gotchas.mdA bundle deployed cleanly but grants or hides the wrong thing
references/examples.mdSizing a persona model against a worked sales/service example, or naming an anti-pattern you already have
references/well-architected.mdJustifying the design in a review, or citing the official sources behind a claim
references/llm-anti-patterns.mdReviewing permission-set output an assistant generated

Package files