Invoke-PimMspBuild
Build a managing or managed tenant (MSP build)
Part of the PIM Manager release - not a standalone download; it runs via the product's own install command or the updater.
Purpose
Build a managing tenant (an MSP's own PIM Manager that publishes a signed baseline) or a managed tenant (a customer's PIM Manager that pulls it) end to end from one configuration file of ids and names. Without -Apply it only prints the plan.
When to run it
When an MSP sets up its managing tenant, and when a managed tenant is set up (the guided install runs it for a managed tenant with an enrollment key). Order: build the managing tenant, build the managed tenant (it prints its subnet id), add that id to the managing tenant's config and re-run -From network-<n>. Run it without -Apply (or with -WhatIf) first.
Who may run it
| Kind | Role | Scope | Why |
|---|---|---|---|
| azureRbac | Owner | the target subscription | The hosting step creates the resources and assigns Azure roles to the identities (Contributor + User Access Administrator is the equivalent). |
| entraRole | Privileged Role Administrator | tenant | Microsoft Graph application permissions for the tick, Manager, publish and pull identities; members of the role-assignable SQL admin group. Global Administrator also works. |
| entraRole | Application Administrator | tenant | The Manager's sign-in app registration and the Invardia Support app's ownerships (Application.ReadWrite.OwnedBy). |
| entraRole | Groups Administrator | tenant | The SQL admin group and the Manager's members group. |
| exchange | Exchange Administrator | Exchange Online (optional) | Only when the mail sender is provisioned by this build. |
| other | Signed in to az as yourself (no deployIdentity in the config), or the certificate deploy identity named in the config | the tenant and subscription in the config | The build refuses another tenant, another subscription, a signed-in service principal, and any variable that would make a step authenticate as somebody else. The config may hold no secret. |
What it changes
| Where | Action | What | Permission / role | Scope | Why | Undo |
|---|---|---|---|---|---|---|
| Invardia | create | the enrollment of this managed tenant (only with an enrollment key; step enroll) | Invardia creates the environment, signs the licence, issues an install key and returns the managing tenant's bundle address and signing key ids. | Ask the managing company / Invardia support to remove the environment. | ||
| Azure | create | the whole hosting (step hosting = Invoke-PimDeployAll.ps1 -Scenario S3 or S6): resource group, network, registry, Log Analytics, SQL, Container Apps environment, the Manager, the tick / bootstrap / updater jobs, the sign-in app, the SQL admin group, schema, mail sender, code | Each tenant runs its own PIM Manager in its own subscription. | Delete the resource group; see https://invardia.com/docs/pim/scripts/Invoke-PimDeployAll/ for every object. | ||
| Azure | create | tenant Key Vault + the bootstrap identity's read grant (step keyvault, only when keyVaultName + bootstrapAppId are set) | The managing tenant's own vault. | Delete the vault (soft-delete applies). | ||
| Entra ID | grant | the build's own identity: the certificate application in certificate mode, the signed-in administrator in signed-in mode (step sqlgroup; Initialize-PimSqlAdminGroup.ps1). The Invardia Support app is never added as the Support app; the tick and the Manager use their own least-privilege database users | member of grp-pim-sql-admins (members/$ref) | the SQL admin group | The build's store steps (access, scenario, licence, registry and the end-of-install check) run as that identity. | Remove it from the group. Certificate mode: the build's last step (sqlgrouprelease) removes its own membership and reads it back; on by default (the config's releaseOwnBuildMembership = false keeps it). |
| Microsoft Graph | grant | the tick identity (engine set) and the Manager identity (read-only set) (step access; Initialize-PimHostingAccess.ps1) | Microsoft Graph application permissions: the engine set incl. RoleManagement.ReadWrite.Directory, AppRoleAssignment.ReadWrite.All, Application.Read.All, RoleManagement.ReadWrite.Defender, DeviceManagementRBAC.ReadWrite.All, AccessReview.Read.All (appRoleAssignments); the read-only set for the Manager; never a tenant-wide Mail.Send | tenant | The engine applies access; the Manager reads. | Remove the app role assignments. |
| Azure RBAC | grant | the Manager identity | Container Apps Jobs Operator | the tick job | The Manager may start the engine (Run now / commit). | Remove the role assignment. |
| Azure RBAC | grant | the Manager identity (step access; Initialize-PimHostingAccess.ps1; verified and repaired by the end-of-install check) | Reader | the PIM resource group only | The Environment report reads the installation's own architecture (apps, jobs, registry, SQL, Key Vault, network). | Remove the role assignment (the Environment report then shows the architecture as not readable). |
| Azure RBAC | grant | the tick identity | Reader (+ User Access Administrator only when opted in) | tenant root management group | Azure discovery across subscriptions. | Remove the role assignment. |
| Azure SQL | update | pim.Settings SchedulerTickJobId, the scenario, the licence + install key (steps access, scenario, licence; Set-PimScenario.ps1, Set-PimLicense.ps1) | The environment knows its engine job, its role in the MSP pair and its licence. | Change them in Settings. | ||
| Azure SQL | grant | the Invardia Support app (step supportaccess when support.appId is set; Grant-PimSupportAccess.ps1) | a contained database user (CREATE USER) in the read role pim_support_read only, at every level; setup: also admin rights (db_owner) inside an open support session only, at most 8 hours. No SQL admin group membership and no owner entry on PIM's app registrations or groups | the PIM database | Invardia support can help (and at 'setup' repair inside a support session). | Grant-PimSupportAccess.ps1 -Remove. |
| Azure SQL | create | managing tenant registry schema: platform.Tenants, pim.CentralAdmins, targeting columns; one row per managed tenant with its ring and tags (steps registry, register-<n>; Initialize-PimMasterRegistry.ps1, Register-PimManagedTenant.ps1) | Which tenants the managing tenant publishes to. | Remove the tenant rows in the Manager. | ||
| Azure | create | managing tenant: storage service endpoint on its subnet, the signed-baseline storage account + container (anonymous blob read; its own subnet allowed, then firewall Deny), a storage network rule per managed tenant subnet (steps publishnetwork, storage, network-<n>; New-PimBaselineStorage.ps1, Set-PimBaselineNetworkAccess.ps1) | Public-but-signed: managed tenants read the bundle over a network the firewall names; trust is the signature. | Delete the storage account; remove the network rules. | ||
| Azure RBAC | grant | the publish job's managed identity (and the signed-in builder for the first upload) | Storage Blob Data Contributor | the baseline container | The publish job writes the signed bundle. | Remove the role assignment. |
| Azure | create | a non-exportable RSA signing key in Key Vault (step signingkey; New-PimBaselineSigningKey.ps1); the key id is printed for the managed tenants to pin | The bundle is signed; managed tenants refuse a bundle that does not verify. | Disable / delete the key (managed tenants then refuse new bundles). | ||
| Azure RBAC | grant | the publish job's managed identity (and the builder to read the key) | Key Vault Crypto User (Key Vault Reader for the builder; Key Vault Administrator or Key Vault Crypto Officer are what the person needs to create the key) | the signing key / vault | The publish job signs with the key; nobody can export it. | Remove the role assignments. |
| Azure | create | the publish job ca-pim-publish (daily + on demand, system identity) and its first run (steps publishjob, publish; Deploy-PimBaselinePublishJob.ps1, Start-PimBaselinePublish.ps1) | The managing tenant publishes the signed baseline in the cloud, no signing host. | Delete the job. | ||
| Azure SQL | grant | the publish job's identity | a database user with db_datareader (db_datawriter / db_ddladmin / db_owner are refused for it) | the managing tenant's PIM database | It reads what to publish, nothing else. | DROP USER for the publish job. |
| Azure | create | managed tenant: storage service endpoint on its subnet (the subnet id is printed for the managing tenant), the pull job (plain blob URL, signature-verified, system identity) with AcrPull on the registry (steps pullnetwork, downlink; Initialize-PimBaselinePullNetwork.ps1, Deploy-PimDownlinkJob.ps1) | The managed tenant pulls the bundle; retraction is report-only. | Delete the pull job; remove the service endpoint. | ||
| Microsoft Graph | grant | the pull job's managed identity | the engine set of Microsoft Graph application permissions (incl. RoleManagement.ReadWrite.Directory) + member of grp-pim-sql-admins | tenant / the SQL admin group | It applies the managing tenant's baseline in this tenant. | Remove the app role assignments and the membership. |
| Azure | create | private networking options (only when configured): the SQL build window AllowSetupHost (opened and closed by the build), a private endpoint for the bundle store, privatelink DNS (Set-PimSqlBuildWindow.ps1, Set-PimBaselinePrivateEndpoint.ps1, Initialize-PimBaselinePrivateDns.ps1, Close-PimSqlSetupHostRule.ps1) | Private stores are reached only while the build needs them. | Remove the private endpoint / DNS records; the firewall rule is removed by the build's last step. | ||
| Invardia | update | self-service managed tenants: the bundle facts (address, signing key ids) and a managed tenant's pull subnet id (steps enrollment, enrollreport; Publish-PimEnrollmentFacts.ps1) | Invardia hands them to the other side of the pair. | Ask Invardia support. |
What it only reads
- The config (validated: no key or value may look like a credential), the plan (engine/msp/PIM-MspBuild.ps1), the signed-in identity (az account show, a token's tenant and user-ness), the managed identities' object ids when a step needs them, the running Manager image on a resume.
- Directory roles named only to say who must run a step it cannot: Global Administrator, Privileged Role Administrator, Application Administrator, Groups Administrator, Exchange Administrator.
- The end-of-install check (Confirm-PimInstall.ps1) reads every result back; a required line that would fail ends the build with its fix.
Parameters
| Parameter | Required | Default | What it does |
|---|---|---|---|
| -Role | yes | Master (managing tenant, S3) or Slave (managed tenant, S6). | |
| -ConfigPath | yes | The build config: ids and names only (no secret is accepted). | |
| -Apply | no | false | Build. Without it (or with -WhatIf) the plan and the operator steps are printed. |
| -From | no | Resume at this step id. | |
| -UpdateSourceUrl | no | The release feed URL template (a credential: never printed or saved). | |
| -UpdateSourceUrlFile | no | $env:PIM_UPDATE_SOURCE_URL_FILE | A one-line file holding the release feed URL. |
| -EnrollmentKey | no | Managed tenant: the Invardia enrollment key (never printed or stored). | |
| -InvardiaBaseUrl | no | https://invardia.com | Invardia address for the enrollment. |
| -EnrollmentTimeoutSeconds | no | 900 | How long to wait for the signed licence. |
| -ImageTag | no | the config's imageTag, else the running image on a resume | The image tag the build uses. |
| -InvardiaInstallToken | no | $env:INVARDIA_INSTALL_TOKEN | Tracked install: every step is reported to Invardia. |
| -InvardiaInstallEventsUrl | no | https://invardia.com/api/install/events | Where tracked steps are reported. |
| -OnStep | no | Callback per step event (the guided install's reporter). | |
| -ResumeCommand | no | The one resume line shown when a step fails (the guided install / Invardia's bootstrap): the failure is then told in three parts, without the operator steps. | |
| -ReleaseOwnBuildMembership | no | on | Certificate mode: the build's own identity leaves the SQL admin group at the end of the build (step sqlgrouprelease; only that membership, read back). On by default; the config key releaseOwnBuildMembership = false keeps the membership. A later resume that needs the store then starts at -From sqlgroup. |
Preview with -WhatIf
Prints the plan (every step with the script it runs, NOT RUNNABLE steps with the reason), the operator steps, and one 'What if:' line per step naming what it would change and in which tenant and subscription. -WhatIf wins over -Apply: no claim at Invardia, no sign-in, nothing created or granted. Each step's own script can be previewed with its -WhatIf.
Undo
Delete each tenant's resource group (export the database first) and the Entra objects the hosting step created; on a managing tenant also the baseline storage, the signing key and the publish job, and the managed tenants' rows; Grant-PimSupportAccess.ps1 -Remove for the Invardia Support app. See each change's undo.
Network
It connects to: management.azure.com, graph.microsoft.com, login.microsoftonline.com, <server>.database.windows.net (TCP 1433), <storage>.blob.core.windows.net (the signed baseline), <vault>.vault.azure.net (the signing key), invardia.com (enrollment, enrolled-tenant facts, tracked install), the Azure CLI extension index (azcliextensionsync.blob.core.windows.net): az installs its 'containerapp' extension once when missing (hosting step) -- az's own download, checked against the index's sha256
How it runs
Part of the PIM Manager release - not a standalone download; it runs via the product's own install command or the updater.
Release 3.0.70
Every script, identity and permission for your security review: PIM Manager