SfSkills Domains Docs GitHub

Home / admin / managed-package-installation-and-upgrade

managed-package-installation-and-upgrade

Use when a subscriber admin is installing, upgrading, or rolling back a managed package (1GP or 2GP) sourced from AppExchange or a private install URL — covers pre-install evaluation, sandbox dry runs, license and dependency checks, the install/upgrade audit trail, post-install configuration the package's InstallHandler cannot perform, and uninstall fallback.

Open the skill folder on GitHub


Managed Package Installation and Upgrade

Activate when a Salesforce admin is responsible for installing a third-party managed package, upgrading an installed package version, or unwinding a failed install. The skill produces a readiness assessment, an environment-by-environment install runbook, a post-install configuration checklist, and an uninstall fallback. Publisher-side concerns (building the package, authoring InstallHandler, push-upgrade scheduling) belong to devops/managed-package-development and are out of scope here.


Before Starting

Gather this context before touching any install URL:


Questions to Ask Before Configuring

Ask these before the install URL is opened or the pipeline stage is written. Each one maps to a failure that is cheap now and expensive after the package is in production.

AskWhy it mattersWhat a good answer adds
"Which exact 04t version, and is it Released or Beta?"The version ID is the only unambiguous identifier; release state is queryable only in the publisher's org, from MetadataPackageVersion.ReleaseStateA pinned version in config/package-inventory.json instead of "latest"
"Is this package certified — did it pass AppExchange security review?"Certified packages get their own per-transaction governor limits; non-certified packages spend yours, and only certified/unlocked packages can be push-upgradedA limits impact assessment for the objects the package shares with your automation
"Who is the named owner of this package in our org, and who pays for the seats?"Licences expire and seats run out independently of object permissions; an unowned package is one nobody renewsAn owner row in the inventory and a renewal date on someone's calendar
"How many users need a seat, versus how many need the permission set?"PackageLicense.AllowedLicenses is a hard ceiling; a user can hold every object permission and still be locked out with no seatThe seat count to procure, and the reconciliation query to run after install
"Does the package ship Remote Site Settings, CSP Trusted Sites, or Named Credentials?"activateRSS defaults to false, and secrets cannot be set by the publisher's install scriptThe pre-staging steps that must precede the install, and the endpoints to review
"Will the install be clicked in Setup, deployed as metadata, or run from the CLI?"The three paths differ in what they support and in what they default to — the Metadata API defaults securityType to AllUsers, the CLI defaults it to AdminsOnlyAn explicit audience decision written into the artefact, not inherited from a default
"What subscriber code, Flows, and validation rules reference this namespace today?"Those references break on a rename in an upgrade and block an uninstall outrightThe reference list the checker produces, refreshed before every version change

What a proper configuration adds over just clicking Install: the audience and the remote-site state are explicit rather than defaulted, seats are reconciled against the permission set rather than assumed, and the version, owner, licence expiry, and namespace references are recorded in an artefact the next upgrade can be planned from.


Core Concepts

Install URL Anatomy

A package install URL takes one of two shapes:

The version-ID suffix is the load-bearing piece. Two URLs that differ only after ?p0= are two different package versions and may differ materially in metadata or code.

The Three Install Paths

PathCoversAudience controlWhere it is grounded
Setup UI — Installed Packages1GP and 2GPThree radio buttons, chosen at click timeThe install page itself
Metadata API — InstalledPackage1GP onlysecurityType, defaults to AllUsers when omittedapi_meta.txt L81389–81451
CLI — sf package install1GP, 2GP, unlocked--security-type, defaults to AdminsOnlysf package install --help, @salesforce/cli/2.149.9

The Metadata API type is explicit that it "represents a first-generation managed package" and that 2GP and unlocked packages install through the CLI instead. Two constraints follow the metadata path: an InstalledPackage deployment cannot contain any other metadata type, and a single deployment installs at most 20 of them. Uninstall is asymmetric too — sf package uninstall is documented as a second-generation command, and 1GP removal goes through Setup.

Deployable XML, package.xml, and the full CLI flag table are in references/metadata-examples.md.

Install Audience Choice

The install dialog presents three radio buttons:

ChoiceWhat it doesWhen to use
Install for Admins OnlyGrants object/field access only to System Administrator profileDefault-safe choice; defer user access to Permission Set assignment later
Install for All UsersGrants access via the package's bundled profile settings to every internal userOnly if the package documentation says so AND you've validated profile settings in sandbox
Install for Specific ProfilesPer-profile grant configurationRare; the matrix is hard to maintain and Permission Set Groups are the modern alternative

The modern recommendation: Install for Admins Only, then grant feature access via Permission Sets / Permission Set Groups the package ships. Avoid baking access into existing profile settings — it makes future audits and least-privilege reviews painful.

Only the first two choices exist outside the UI: the Metadata API securityType field and the CLI --security-type flag both accept exactly AdminsOnly and AllUsers. A scripted install therefore cannot express "Install for Specific Profiles" — plan that grant as a separate Permission Set deploy.

IP Protection Visibility Rules

In a managed-1GP-installed subscriber org, the following are invisible or uneditable:

Subscribers can:

Post-Install Configuration Gap

The publisher's InstallHandler Apex class runs during install with subscriber-org context but cannot:

These items live in the post-install checklist the publisher's documentation should provide. If it doesn't, write your own from the package's package.xml (visible after install via Setup → Installed Packages → View Components).

Upgrade Semantics

Upgrades preserve subscriber data and unmanaged additions, but:

Push Upgrades vs. Pull Upgrades

MechanismInitiated bySubscriber consentUse case
Pull upgradeSubscriber clicks install URL, deploys InstalledPackage, or runs sf package installYes — the audience choice is made at install timeMajor versions, anything with new licensing
Push upgradePublisher creates a PackagePushRequest with a ScheduledStartTimeNo click, but blockable — see belowPublisher-driven fixes and coordinated migrations

If a package version changes in a subscriber org without anyone installing it, it was a push.

Push is not unconditional. It is available only to 1GP and 2GP managed packages that have passed AppExchange security review, and to unlocked packages, where it is on by default. The subscriber org can refuse: the publisher's PackageSubscriber record carries HasRestrictionEnabled, which "indicates whether the subscriber org has blocked push upgrades," and a CustomUpgradeType value of BlockedBySubscriber. Both default to off, so the usual state is "not blocked," not "cannot block." Scheduling is also approximate — Salesforce documents that a push may start hours after the requested time, and orgs whose OrgStatus is Inactive never receive one (references/gotchas.md, Gotcha 2).

Licences Are Not Permissions

A package licence and an object permission are separate grants, and an install produces neither automatically. PackageLicense holds one row per licensed managed package with AllowedLicenses, UsedLicenses, ExpirationDate, and a Status restricted to Active, Expired, Free, Trial; it is read-only through the API. UserPackageLicense holds the per-user seat and does support create() / delete(), so seat assignment is DML. A user with every field permission and no seat is locked out, and the platform reports it as the DML status code LICENSE_LIMIT_EXCEEDED only when you have already run out. The reconciliation queries and an Apex assigner are in references/metadata-examples.md §6.

Governor Limits Change Shape After Install

A certified managed package — one that passed AppExchange security review — gets its own set of per-transaction limits: its Apex has its own 150 DML statements and its own 100 synchronous SOQL queries on top of yours. A non-certified package gets no separate budget and spends your org's. Four limits are never duplicated and count for the whole transaction no matter how many packages run in it: total heap size, maximum CPU time, maximum transaction execution time, and the maximum number of unique namespaces. Cumulative cross-namespace ceilings are 11× the per-namespace limit (references/gotchas.md, Gotcha 12). Certification status therefore belongs in the pre-install assessment, not just in the security conversation.


Common Patterns

Pattern: Sandbox-First Install Runbook

When to use: Any new package installation, any upgrade of a package that touches business-critical data, any package new to your org governance.

How it works:

  1. Install in a Developer Sandbox (or scratch org) first. Note the install duration, components added (Setup → Installed Packages → View Components), and any warnings.
  2. Run a smoke test of the org's existing custom code that references the package's namespace or public methods. Compile-time breaks surface here.
  3. Install in a Full Sandbox refreshed from production. This catches data-volume-dependent issues the Developer Sandbox misses.
  4. Install in production during a low-traffic window. The runbook for production is the sandbox runbook with the install URL switched from test.salesforce.com to login.salesforce.com.

Why not the alternative: "Install directly in production because it's just a managed package" is the most common cause of post-install support tickets. Even a Security-Reviewed package can interact badly with org-specific automation.

Pattern: Install for Admins Only + Permission Set Group Grant

When to use: Default for every install. Reverse only if the package documentation explicitly says otherwise and you've validated.

How it works:

  1. At install time, choose Install for Admins Only.
  2. After install, find the Permission Sets and Permission Set Groups the package created (Setup → Permission Sets, filter by namespace).
  3. Build a subscriber-org Permission Set Group that combines: a relevant package Permission Set + your org's role-specific Permission Sets.
  4. Assign the Permission Set Group to users via Setup → Permission Set Groups → Manage Assignments, or via SOQL-driven assignment if user count is high.

Why not the alternative: Install for All Users grants access via the package's profile settings, which bypass your org's principle-of-least-privilege model and are hard to revoke selectively.

Pattern: Uninstall with Data Export

When to use: A package is being removed because it's no longer licensed, the vendor pivoted, or a competing product replaced it.

How it works:

  1. Export package-introduced data first. Setup → Installed Packages → click the package name → View Components. Identify all Custom Objects added by the package (rows with the package namespace). For each, run a Data Export via Data Loader or Bulk API — these records will be permanently deleted when the package is uninstalled.
  2. Identify subscriber-written code that references the package. Search the org's Apex, Flow, and Validation Rules for the package namespace. Each reference must be removed or refactored before uninstall, or the uninstall fails with "component is referenced."
  3. Run uninstall in sandbox first. Setup → Installed Packages → Uninstall. Salesforce produces an export of package data as a CSV that's emailed to the running admin and retained for 48 hours. This is the only post-uninstall recovery path.
  4. Verify org integrity in sandbox — broken page layouts, missing Flow elements, failed scheduled jobs.
  5. Repeat in production during a low-traffic window. The 48-hour export retention is your only undo button.

Why not the alternative: "Just uninstall" loses any data in package-introduced Custom Objects irrecoverably after the 48-hour window.


Decision Guidance

SituationRecommended ApproachReason
New package, never installed in any orgDeveloper sandbox → Full sandbox → Production, with smoke test at each stepCatches integration and data-volume issues progressively
Patch upgrade (X.Y.Z → X.Y.Z+1)Sandbox install if possible; production proceeds even if sandbox not runPatches are bug-fix-only and limited in metadata surface; push patches may not give sandbox window
Minor upgrade (X.Y → X.Y+1)Mandatory sandbox-first with smoke testNew features may add required fields or new Permission Sets to manage
Major upgrade (X → X+1)Mandatory sandbox-first + UAT + release-train slotLikely breaking changes; treat as a project, not a maintenance task
Package targets a feature you don't license yetDo not install until license SKUs are confirmedInstalling without licenses leaves users locked out and creates support-ticket noise
Package needs Named Credential secrets at install timePre-create the External Credential principal in target org before installInstallHandler cannot set client secrets; UI step must precede or follow install per docs
Removing a packageExport package object data, audit subscriber references, sandbox-uninstall first48-hour data recovery window is non-negotiable; subscriber code references block the uninstall
Package is not AppExchange-certifiedModel its Apex against your own governor budget before install, and expect no push-upgrade optionNon-certified namespaces get no separate per-transaction limits and cannot be push-upgraded
Install must run from a pipeline, not a clickSource-track installedPackages/<ns>.installedPackage-meta.xml with securityType and activateRSS explicit, in its own single-type manifestThe metadata path defaults to AllUsers and inactive remote sites; the type cannot share a manifest
Package is 2GP or unlockedsf package install; do not reach for InstalledPackageThe metadata type represents first-generation managed packages only
Users have every permission and still cannot use the featureCheck PackageLicense.Status, ExpirationDate, and AllowedLicenses vs UsedLicenses before touching permissions againSeats and permissions are independent grants; an install assigns neither

  1. Establish the baseline. Run sf package installed list --target-org <env> --json for every environment and the PackageLicense query in references/metadata-examples.md §6. Write the result into config/package-inventory.json in the shape shown in §7 — namespace, version per environment, owner, licence counts, expiry, certification status.
  2. Lint the baseline before planning anything. python3 scripts/check_managed_package_installation_and_upgrade.py --manifest-dir . reports expired or over-allocated licences, version drift between environments, packages with no owner, installedPackage files with an implicit securityType, permission sets referencing a namespace that is not in the inventory, and the subscriber code that references each namespace. Resolve the drift and ownership findings before writing the runbook, not after.
  3. Resolve the version and its properties. Pin the exact 04t, confirm Released rather than Beta, and confirm AppExchange certification — certification decides both the governor-limit shape (references/gotchas.md, Gotcha 12) and whether push upgrades are possible at all. Record both in the inventory.
  4. Author the install artefacts. Write installedPackages/<ns>.installedPackage-meta.xml with versionNumber, securityType, and activateRSS all explicit, and its own single-type package.xml, from references/metadata-examples.md §2–3. Write the subscriber-owned Permission Set from §5 as a separate deploy — the install manifest cannot carry it.
  5. Dry-run, then install, sandbox first. sf project deploy start --source-dir force-app/main/default/installedPackages --target-org <sandbox> --dry-run, then the real deploy. Run the org's existing Apex tests against the new namespace; record the install duration in a full sandbox that mirrors production volumes.
  6. Execute the post-install checklist. The items the publisher's install script cannot do — Named Credential and External Credential secrets, Flow version activation, Custom Metadata seeded from subscriber data, seat assignment via UserPackageLicense, Permission Set Group assignment. Sequence them in templates/managed-package-installation-and-upgrade-template.md.
  7. Verify, then re-baseline. Run the three checks in references/metadata-examples.md §8: the version that actually landed, PackageLicense seats and expiry, and a canary user exercising the packaged tab end to end. Regenerate config/package-inventory.json and re-run the checker so the next upgrade starts from truth.

Review Checklist


Salesforce-Specific Gotchas

  1. The 48-hour uninstall data window is a hard limit. Salesforce emails a CSV export to the uninstalling admin and retains it server-side for 48 hours, then deletes it. There is no recovery path after that. UNVERIFIED (2026-09-05): the 48-hour figure could not be re-confirmed against the doc extracts available here — see references/gotchas.md, Gotcha 1, and never let a runbook depend on it in place of a durable pre-export.
  2. Push upgrades need no subscriber click, and blocking them is opt-in. PackageSubscriber.HasRestrictionEnabled defaults to false, so the default posture is "not blocked." Ask each publisher whether push is enabled for your org.
  3. The publisher's install script runs as the package's own system user. Records it creates are attributed to that user, not to the installing admin — InstallContext.installerId() is the only handle on who ran the install. It cannot access session IDs and can only make callouts asynchronously, after the install commits.
  4. Subscriber-org Permission Sets can be created for managed package access surfaces. Permission Sets that grant access to managed objects/fields are stored in the subscriber org and survive uninstalls (whereas package-bundled Permission Sets are deleted).
  5. API name collisions block install. If the subscriber org has a Custom Object Foo__c and the package introduces pkg__Foo__c, the namespace prefix usually prevents collision — but fields without namespace prefixes on packaged objects can collide with subscriber-added fields on the same object. Inventory before install.
  6. Install for All Users grants via profile settings. The grant is durable across upgrades and cannot be selectively revoked without modifying every affected profile. Permission Set Groups are reversible; profile grants are not, easily.
  7. Beta packages cannot be installed in production. A beta version ID (04t... from a non-released package) only installs into Developer Edition orgs, sandboxes, and scratch orgs. The install URL appears to work but Salesforce blocks the final step. UNVERIFIED (2026-09-05): the block itself is not stated in the doc extracts available here; what is grounded is that MetadataPackageVersion.ReleaseState is Beta or Released, visible only in the publisher org, and that push upgrades refuse a beta version — see references/gotchas.md, Gotcha 4.
  8. activateRSS is required and defaults to false. Deploy an InstalledPackage without it and every Remote Site Setting and CSP Trusted Site the package ships arrives inactive; the package's callouts fail on first real use, long after the install looked clean.
  9. A failed install script aborts the install; a failed uninstall script does not stop the uninstall. In both cases the error email goes to the publisher's Notify on Apex Error address, not to you.
  10. Grants on packaged components cannot use wildcards. Each packaged object and field must be enumerated by name, prefixed with the namespace and two underscores, in the Permission Set XML.

Output Artifacts

ArtifactDescription
Pre-install readiness assessmentVersion ID, audience choice, license posture, footprint inventory, dependency check
Sandbox-first install runbookStep-by-step for Developer Sandbox → Full Sandbox → Production with smoke-test gates
Post-install configuration checklistItems InstallHandler cannot do, with owners and sequencing
Uninstall planData export step, subscriber-code reference audit, 48-hour recovery awareness
Change-log entryVersion, install date, approver, link to post-install runbook execution evidence
config/package-inventory.jsonMachine-readable state per package: namespace, version per environment, owner, seats allowed/used, licence status and expiry, certification, push-block posture, subscriber code references — the input the checker lints and the baseline the next upgrade is planned from
installedPackages/<ns>.installedPackage-meta.xml + single-type package.xmlThe install itself as reviewable, diffable source rather than a click

Reference Files

FileRead it when
references/metadata-examples.mdWriting the deployable installedPackage XML, the single-type package.xml, the sf package install / installed list / uninstall commands with grounded flag defaults, the subscriber-owned Permission Set for packaged components, the PackageLicense / UserPackageLicense reconciliation SOQL and Apex, the config/package-inventory.json artefact, and the three post-install verification checks
references/gotchas.mdThe install succeeded and something is wrong anyway — callouts failing, users locked out with full permissions, a version that changed on its own, your own Apex suddenly hitting governor limits, or an uninstall that left cleanup undone
references/examples.mdPlanning a first install, diagnosing a push upgrade that broke subscriber Apex, or retiring a package with data in it — three worked runbooks plus the inventory row each one produces
references/well-architected.mdJustifying the sandbox-depth or install-audience tradeoff to a reviewer, or locating the official source behind any claim in this skill
references/llm-anti-patterns.mdReviewing install or uninstall guidance an AI assistant produced, especially anything that says "the InstallHandler will handle it" or "Salesforce keeps a backup for 48 hours"
templates/managed-package-installation-and-upgrade-template.mdRunning workflow steps 1 and 6 — the package identity, footprint inventory, environment sequence, and post-install checklist
scripts/check_managed_package_installation_and_upgrade.pyWorkflow step 2 and again at step 7, and before every deploy that touches installedPackages/ or a permission set naming a packaged component

Package files