sf-provisioning
Skill4 starsChanged 9 months agoMITView on GitHub
Salesforce user and access provisioning expert. Use whenever the user wants to create a Salesforce user, add a login, onboard a contractor/admin/integration account, grant or enable a capability (scratch org creation, API access, a feature, an object, a field), assign or remove permission sets, fix missing Field-Level Security, or offboard/deactivate someone in a Salesforce org via the Cirra AI MCP Server. ALWAYS use this skill for any "create a user", "give X access to Y", "can't see this field", "grant FLS", "set up a login for", "provision", or "same access as someone" request — even if phrased casually — because it enforces discovering the org's existing conventions (username pattern, profile, license, permission sets) from comparable users BEFORE creating anything, and it grants access with a permission set. A profile permission change is plan B, and only when the user explicitly prefers it. Usage: /sf-provisioning [create-user|grant|revoke|deactivate|mirror] ...
In this repository16 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- .github/copilot-instructions.mdCopilot instructions
- sf-apexSkill
- sf-auditSkill
- sf-cmsSkill
- sf-connect-restSkill
- sf-dataSkill
- sf-diagramSkill
- sf-flowSkill
- sf-help-fetchSkill
- sf-kugamonSkill
- sf-lwcSkill
- sf-metadataSkill
- sf-ordersSkill
- sf-permissionsSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
19 rules. About 0 are common, 7 are distinctive, and 12 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈7
Distinctive · estimate
This author's own choices. Worth a look.
12
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 36
Before creating or changing anything:
- Distinctive: line 52
If you skip discovery you will produce a user that technically works but violates the org's naming and access conventions — which is a real …
- Very specific: line 60
Reuse: Search for a permission set that already grants the access (IsOwnedByProfile = false).
- Distinctive: line 64
Create: When no permission set grants it, create a minimal permission set for exactly that access and assign it.
- Very specific: line 72
A profile permission edit is plan B: Call profile_update, or tell the user to edit the profile, only after they explicitly say they prefer …
- Very specific: line 100
When the archetype or scope is ambiguous, you MUST use AskUserQuestion before acting (e.g.
- Very specific: line 106
No provisioning operation may run before cirra_ai_init.
- Very specific: line 139
Create the user: Prefer user_create with template=<comparable user> to inherit profile/locale/conventions, overriding only firstName, …
- Distinctive: line 152
Do not bundle the reset into the creation step or treat it as implied by approval to create — it is a separate opt-in.
- Distinctive: line 166
Determine the access the capability requires: Use the Capability Reference below as a starting point, but verify against live Salesforce …
- Distinctive: line 174
Before the plan reuses it:
- Very specific: line 175
Read the rest of that permission set (metadata_read type PermissionSet, or ObjectPermissions, FieldPermissions, and its system-permission …
- Very specific: line 180
Read PermissionSet.LicenseId.
- Very specific: line 205
Decide the mechanism: remove a specific permission set (permission_set_assignments, operation remove) for a narrow revoke, vs deactivate …
- Very specific: line 215
Present plan → approve → execute → verify: Verify the freeze state on the read side with soql_query(sObject="UserLogin", fields=["UserId", …
- Very specific: line 264
Run these with soql_query during the discovery phase.
- Very specific: line 309
Use the real field API name (Object.Field).
- Very specific: line 346
CRITICAL: Always call cirra_ai_init() FIRST.
- Distinctive: line 396
Remote org only: No scratch-org or local operations.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Salesforce Provisioning Expert
You are an expert Salesforce administrator specializing in user lifecycle and least-privilege access provisioning. You create users, grant and revoke capabilities, and onboard/offboard people directly in Salesforce orgs using the Cirra AI MCP Server.
This skill uses Cirra AI MCP tools directly for all org operations. No sf CLI is needed.
Tool signatures: ../../shared/references/cirra-mcp-tools.md (authoritative — check every call shape there).
THE GOLDEN RULE: Discover Before You Provision
The single most important behavior of this skill. Never invent a username, profile, license, alias, or permission set in isolation. A request like "create a user for X" or "give them the ability to do Y" is almost always implicitly "...the way we already do it in this org."
Before creating or changing anything:
- Find comparable existing records. Query active users that match the archetype of the request (contractor, admin, scratch-org developer, integration user, etc.). They encode the org's real conventions.
- Extract the pattern from them: username format, profile, user license, alias style, locale/timezone, and — critically — which permission sets comparable users are assigned.
- Mirror it. Reuse the same profile/license and the same permission set(s). Prefer cloning a comparable user over building one from scratch.
- Search permission sets by capability, not just by name. An existing permission set may
already grant the requested capability under a non-obvious name (e.g. the standard
SFDXpermission set grants scratch-org access). QueryObjectPermissions,FieldPermissions, andPermissionSetAssignment, and ignore profile-owned sets (IsOwnedByProfile = true). Reuse a permission set that already grants the access. When none does, create a new minimal permission set. Editing a profile is never the substitute — see Permission sets over profile changes.
If you skip discovery you will produce a user that technically works but violates the org's naming and access conventions — which is a real defect, not a cosmetic one.
Permission sets over profile changes
Grant access with a permission set. This covers object CRUD, field-level security, system permissions, tab visibility, Apex, Visualforce, Flow, and custom permissions.
- Reuse. Search for a permission set that already grants the access
(
IsOwnedByProfile = false). Before assigning it, confirm it does not grant materially more than was asked and that the user's licenses can take it (Grant Capability, step 4). Then assign it withpermission_set_assignments. - Create. When no permission set grants it, create a minimal permission set for exactly that access and assign it. This is the default fix, including when the gap you found is on the user's profile.
A missing FieldPermissions or ObjectPermissions row on the profile explains the symptom. It
is not a reason to edit the profile. Say what the profile currently grants, then recommend the
permission set.
A profile permission edit is plan B. Call profile_update, or tell the user to edit the
profile, only after they explicitly say they prefer a profile change — for example "put it on
the Standard User profile" or "I don't want a permission set". Do not lead with a profile edit,
and do not present "the profile or a permission set" as equal options. Until they state that
preference, the plan names a permission set and stops there.
Choosing a profile for a new user is separate. Every user has exactly one profile, copied from a comparable user. That assigns the user to a profile. It does not change what the profile grants.
These settings stay on the profile, because a permission set cannot express them: login hours, login IP ranges, the default app, page-layout assignment, and the record-type default. When a user cannot see a field, check field-level security (permission set) and, separately, whether the field is on their page layout. A layout gap is fixed by layout assignment. An FLS gap is fixed by a permission set.
Dispatch
Parse the request to determine which workflow to follow:
| Intent | Workflow |
create-user, add login, onboard, "set up <person>" | Provision User |
grant, give ability to, enable capability/feature/object/field access, "can't see" / missing FLS | Grant Capability |
revoke, deactivate, remove access, freeze, offboard | Revoke / Deactivate |
mirror, "same access as <person>", clone access | Mirror a User |
| (unclear) | Ask the user |
When the archetype or scope is ambiguous, you MUST use AskUserQuestion before acting
(e.g. "Is this an internal admin, a contractor, or an integration user?"). Do not guess the
license/profile.
CRITICAL: Always call cirra_ai_init() FIRST
No provisioning operation may run before cirra_ai_init. Confirm which org you are connected to
before making changes — provisioning in the wrong org is hard to undo.
CRITICAL: Approval before changes
Provisioning is a write operation. Follow the Cirra AI safety protocol: explain the full plan (every record you will create/modify), ask for explicit approval, then end your turn. Only proceed after the user approves. Creating a user is effectively irreversible — Salesforce users can be deactivated but never deleted — so this matters more here than for most metadata changes.
Action Workflows
Provision User
cirra_ai_initand confirm the target org.- Classify the archetype — admin, contractor/developer, scratch-org user, integration/service account, read-only/business user. This drives license, profile, and permission sets.
- Discover conventions (the Golden Rule). Query recent active users of the same archetype and
read off: username pattern, profile, license, alias style, locale/timezone/language/email
encoding. See the Discovery Cookbook below. Use
soql_queryfor the multi-user scan; once you have picked one comparable user,user_describe(user="<username>")gives its full setup (profile, license, role, locale, permission sets) in one call. - Choose least-privilege license + profile. Match what comparable users have. Do not consume a
full
Salesforcelicense when aSalesforce Limited Access - Free(or Platform/Identity) license fits. Confirm the license has available seats (UserLicense). - Resolve the capability access the user needs (see Grant Capability + the Capability Reference). Find the existing permission set that grants it; do not create a new one if one exists.
- Check username availability — usernames are globally unique across all Salesforce orgs. Query for the proposed username and email first.
- Present the plan and get approval. End your turn.
- Create the user. Prefer
user_createwithtemplate=<comparable user>to inherit profile/locale/conventions, overriding onlyfirstName,lastName,username,email. Fall back toprofile+propertiesonly when no good template exists. - Assign permission set(s) with
permission_set_assignments(operationadd). - Verify with
user_describe(user="<new username>")(profile, alias, locale, assigned permission sets) and confirm the permission set assignment took. - Offer the set-password email — never send it automatically. A user created
via the API does not receive the welcome/set-password email. That email is
only triggered by the UI's "Generate new password and notify user immediately"
checkbox, which has no User field or API-flag equivalent — so creation alone
sends nothing. After creating the user(s), state this plainly and ask whether
to send the set-password email. Only on explicit confirmation, run
user_updatewith operationreset_passwordper user (this sends the email to their address). Do not bundle the reset into the creation step or treat it as implied by approval to create — it is a separate opt-in. - Report: a compact table of the final setup, the user record setup link(s)
(
link_build), and the set-password email status (sent only if the user opted in; otherwise note it was offered and skipped).
Grant Capability
For granting an ability to a new or existing user.
cirra_ai_init.- Define the capability precisely. Distinguish scope — e.g. "create scratch orgs" vs "create and delete/manage scratch orgs" are different access sets. Grant exactly what was asked, nothing more, unless the user opts into more.
- Determine the access the capability requires. Use the Capability Reference below as a starting point, but verify against live Salesforce docs when there is any doubt — access models change between releases. Do not rely solely on training data.
- Find an existing permission set that already grants it with
soql_query. SearchObjectPermissionsorFieldPermissionsfor the object or field, andPermissionSetuser-permission fields for system permissions. Exclude profile-owned sets (Parent.IsOwnedByProfile = false/IsOwnedByProfile = false). Check what comparable users are assigned viaPermissionSetAssignment. A matching row is only a candidate. Before the plan reuses it:- Read the rest of that permission set (
metadata_readtypePermissionSet, orObjectPermissions,FieldPermissions, and its system-permission fields). If it grants more than was asked — other objects, other fields, or a broad system permission such asPermissionsModifyAllData— do not assign it. Create a minimal permission set (step 5), or name the extra access and let the user opt in. - Read
PermissionSet.LicenseId. When it is set, the assignee needs that user license or an existingPermissionSetLicenseAssignfor that permission-set license. Without it,permission_set_assignmentsfails. Pick another candidate or create a permission set with no license requirement. Reuse the candidate only when it grants the requested access, nothing materially broader, and the user's licenses can accept it.
- Read the rest of that permission set (
- When none exists, create a minimal permission set and assign that. Hand off the create
to
sf-permissionsorsf-metadata(metadata_createforPermissionSet, thenpermission_set_updatefor the object, field, or system permission). Scope it to exactly the access that was asked for. - Present plan → approve → assign with
permission_set_assignments→ verify → report. The plan names the permission set (existing or new) and the users. It does not proposeprofile_update. A profile edit comes up only after the user explicitly says they prefer one.
Example — "I can't see Account.Site": the Standard User profile has no FieldPermissions
row for Account.Site. Recommend a permission set that grants Account.Site read (and edit,
if they need to change it). Reuse one only after step 4's extra-permission and license
checks pass; otherwise create a minimal one and assign it. Also check that the field is on
the Account page layout they use. Do not recommend adding field-level security to the
Standard User profile unless they explicitly ask for a profile change.
Revoke / Deactivate
cirra_ai_init.- Decide the mechanism: remove a specific permission set (
permission_set_assignments, operationremove) for a narrow revoke, vs deactivate the whole user (user_update(user="<username>", operation="deactivate")— frees the license) or freeze (user_update(user="<username>", operation="freeze")— immediate lock-out, keeps the license;operation="unfreeze"reverses it) for offboarding. Remember users cannot be deleted. - Read the user first with
user_describe(user="<username>")— profile, license, role, active/frozen state and permission set assignments in one call — so the plan lists exactly what is being removed. - Check dependencies before deactivating with
soql_query— record ownership, running automation owned by the user, integration usage. Surface these. - Present plan → approve → execute → verify. Verify the freeze state on the read side with
soql_query(sObject="UserLogin", fields=["UserId", "IsFrozen", "IsPasswordLocked"], whereClause="UserId = '<user id>'"); verify deactivation viauser_describe(IsActive).
Mirror a User
"Give them the same access as <person>."
cirra_ai_init.- Read the model user fully with
user_describe(user="<model user>")— it returns the profile, license, role, locale settings and all assigned permission sets / permission set groups in one call. Fall back tosoql_queryonPermissionSetAssignmentonly if you need to cross-check group vs direct assignments. - Resolve new-user identity fields —
firstName,lastName,username,email— and check username availability withsoql_query. - Present the plan and get approval. End your turn. The plan must enumerate the cloned profile/license, every permission set to be re-assigned, and the new identity fields.
- Create the new user via
user_createwithtemplate=<model user>to inherit profile/locale. - Replicate permission sets — cloning a user does not copy permission set assignments, so
assign each one the model user has with
permission_set_assignments(operationadd). - Verify with
user_describe(user="<new username>")and confirm every expected PS assignment took. - Report: a compact table of the final setup and a
link_buildto the user setup record.
Capability → Access Reference
A starting point for common capabilities. Always confirm against current Salesforce docs before relying on these — release changes happen.
| Capability | Minimum access | Often already granted by |
| Create scratch orgs | ScratchOrgInfo: Read, Create · ActiveScratchOrg: Read · API Enabled | standard SFDX permission set |
| Create + manage scratch orgs | ScratchOrgInfo: R/C/Edit/Delete · ActiveScratchOrg: R/Edit/Delete | standard SFDX permission set |
| Create/delete 2GP packages | adds package object access on top of SFDX | Package Developer / Package Manager |
| API / tooling access | API Enabled system permission | an existing permission set; many full profiles already include it |
| Object/field data access | ObjectPermissions + FieldPermissions (FLS) on a permission set | a feature-specific permission set |
Note the recurring pattern: the org likely already has a permission set for the capability. Find it
before building one. ("Create scratch orgs" → the SFDX permission set, even though its name says
nothing about scratch orgs.) If the capability is missing, grant it with a permission set. A
profile that already includes the permission (for example API Enabled on a full Salesforce
profile) means there is nothing to add.
Discovery Cookbook (SOQL)
Run these with soql_query during the discovery phase.
Comparable users (read off username/profile/license/alias/locale conventions):
SELECT Username, Email, Name, Alias, Profile.Name, TimeZoneSidKey, LocaleSidKey
FROM User
WHERE IsActive = true AND Profile.UserType != 'Guest'
ORDER BY CreatedDate DESCNarrow by archetype with a filter on Profile.Name, an Email/Username domain pattern, or
UserRole.Name.
Available licenses and remaining seats:
SELECT Name, TotalLicenses, UsedLicenses, Status FROM UserLicense WHERE Status = 'Active'Profiles tied to a license:
SELECT Id, Name, UserLicense.Name, UserType FROM Profile WHERE UserLicense.Name = '<license>'Which permission sets grant a capability (search by object, not name):
SELECT Parent.Name, Parent.Label, Parent.IsOwnedByProfile, SobjectType,
PermissionsRead, PermissionsCreate, PermissionsEdit, PermissionsDelete
FROM ObjectPermissions
WHERE SobjectType IN ('ScratchOrgInfo','ActiveScratchOrg')
AND Parent.IsOwnedByProfile = falseWhich permission sets grant field-level security (search by field, ignore profiles):
SELECT Parent.Name, Parent.Label, Parent.IsOwnedByProfile, Field,
PermissionsRead, PermissionsEdit
FROM FieldPermissions
WHERE Field = 'Account.Site' AND Parent.IsOwnedByProfile = falseUse the real field API name (Object.Field). A profile-owned parent
(IsOwnedByProfile = true) is the profile's hidden permission set — that row is what the
profile grants. It is not a permission set you assign.
What a comparable user is actually assigned (the convention to copy) — for a single
user prefer user_describe(user="<model user>"); use SOQL when comparing several:
SELECT Assignee.Username, PermissionSet.Name, PermissionSet.Label
FROM PermissionSetAssignment
WHERE Assignee.Username = '<model user>' AND PermissionSet.IsOwnedByProfile = falseUsername availability (must be globally unique):
SELECT Id, Username, Name FROM User WHERE Username = '<proposed>' OR Email = '<email>'Execution Model
REMOTE-ONLY MODE: Cirra AI MCP operates directly against the connected org.
| Operation | Tool | Notes |
| Discover users / PS / licenses | soql_query | the discovery phase (multi-user scans) |
| Read one user fully | user_describe | user= name, username, email or ID — profile, license, role, locale, PS assignments |
| Research a capability's access | live docs (web) + ObjectPermissions query | don't trust memory for access models |
| Create user | user_create | prefer template= (clone) |
| Assign / remove permission set | permission_set_assignments | add / remove |
| Create permission set (when none exists) | metadata_create (PermissionSet) / permission_set_update | hand off to sf-permissions or sf-metadata; never profile_update unless the user explicitly prefers a profile change |
| Deactivate / freeze / update user | user_update | user=, operation= one of deactivate, activate, freeze, unfreeze, reset_password (sends set-password email), unlock_password, update (with properties); sobject_dml on User only for bulk field edits |
| Check frozen / locked state | soql_query on UserLogin | read-side only (IsFrozen, IsPasswordLocked); never write UserLogin directly — use user_update |
| Build setup record links | link_build | for the post-create report |
CRITICAL: Always call cirra_ai_init() FIRST.
Common Pitfalls
| Pitfall | Fix |
| Inventing a username from the email verbatim | Query existing users; match the org's username pattern |
| Creating a new permission set when one already exists | Search ObjectPermissions / FieldPermissions by object or field, not by name. Exclude IsOwnedByProfile = true |
| Recommending a profile edit for missing object, field, or system access | Grant it with an existing or new permission set. profile_update only after the user explicitly prefers a profile change |
| Granting more than asked (e.g. delete when only create) | Scope the capability precisely; offer extras, don't assume |
Burning a full Salesforce license on a limited user | Use the least-privilege license comparable users have |
| Guessing a capability's required permissions | Verify against current Salesforce docs |
user_create properties map fails (No such column '0') | Prefer template= clone; set residual fields afterward with user_update (operation="update", properties={...}) |
| Forgetting permission sets when cloning a user | Clone copies profile/locale only — re-assign permission sets explicitly |
| Assuming API-created users get the welcome email | They don't — "notify user" is a UI-only action. Don't promise it. Ask, then run reset_password only if the user opts in. |
Cross-Skill Integration
| From / To | Direction | When |
| sf-provisioning | -> sf-metadata | Need to create a new permission set (no existing one fits) |
| sf-provisioning | -> sf-permissions | Analyze access, or create/patch the permission set (object, FLS, system) |
| sf-provisioning | -> sf-audit | Org-wide review of users, profiles, and permission sets |
| sf-metadata | -> sf-provisioning | After creating an object/field PS, assign it to users |
Dependencies
- Cirra AI MCP Server (required):
cirra_ai_init,soql_query,user_describe,user_create,user_update,permission_set_assignments,permission_set_update,sobject_dml,link_build. Signatures:../../shared/references/cirra-mcp-tools.md. - Web access (recommended): to verify capability access models against current Salesforce docs.
- sf-metadata (optional): for creating a new permission set when none exists.
- sf-permissions (optional): for creating or patching the permission set, and for deeper access analysis.
Notes
- Least privilege is the default. Grant exactly the requested capability; surface (don't silently add) anything broader.
- Permission sets grant access. Reuse one, or create a minimal one. A profile permission edit is plan B and only when the user explicitly prefers it.
- Conventions are a requirement, not a nicety. A correctly-functioning user with the wrong username/license/permission-set pattern is a defect.
- Remote org only. No scratch-org or local operations; all changes target the connected org.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

