Skip to content
InvardiaInvardia
Why InvardiaProductsFeaturesPricingCommunity
Docs
All documentation
Product documentationPIM ManagerSecurityInsight
SettingsPIM Manager settingsSecurityInsight settingsEntra Policy Suite settings
ScriptsPIM Manager scriptsSecurityInsight scriptsEntra Policy Suite scriptsInvardia scripts
Error codesPIM Manager error codesSecurityInsight error codes
API and MCPPIM Manager APIPIM Manager MCP toolsSecurityInsight APISecurityInsight MCP tools
SecurityPIM Manager security packSecurityInsight security packEntra Policy Suite security pack
Network and designPIM Manager network guidesPIM Manager command wizardNetwork egressDesign your installationCalculate your cost
SupportSetup scriptsRemote support access
Search the docs
SupportAboutContactBook a demoCustomer portalDesign your installationFree Pro trial

PIM Manager guides

  1. PIM Manager on a private network: your options for DNS, routing, segmentation and policy
  2. Install PIM Manager in private (internal) mode from scratch
  3. Convert a public (external) PIM Manager environment to private (internal)
  4. Give PIM Manager your own address (custom host name)
  5. Deploy the PIM Manager hybrid worker (PIM for Active Directory) in Azure
  6. Deploy the PIM Manager hybrid worker (PIM for Active Directory) on your own server with Azure Arc

Command wizard

Install PIM Manager in private (internal) mode from scratch

Written by the PIM Manager team for PIM Manager 2.4.579 · 2026-10-10

This guide installs a Single PIM Manager (one tenant) whose Container Apps environment is internal-only. The PIM Manager gets a private IP address in your virtual network. The database and the container registry are reached through private endpoints, and the nightly updater builds on a registry build pool inside the network.

The Invardia guided install builds public mode only. Install-PimManager.ps1 refuses exposure values other than external, with the message “an internal install is a consultant-led setup”. This guide is that consultant-led setup: the same deploy command the guided install uses (Invoke-PimDeployAll.ps1), with the private-mode parameters added.

Managing tenant and Managed tenant builds use Invoke-PimMspBuild.ps1 with "exposure": "internal" in their configuration file. That is covered in the command wizard, not here.

Read first: Network options. It explains each decision this guide asks you to make.


Step 0: Decide and collect

Decision Your value (example) Parameter
Tenant / subscription <tenant-id> / <subscription-id> -TenantId -SubscriptionId
Region: any Azure region (the preflight checks, before anything is created, that every service PIM Manager needs is available there and suggests the nearest region if not) swedencentral -Location
Resource group and names rg-pim, vnet-pim, cae-pim, law-pim, acrpimcontoso, sql-pim-contoso -ResourceGroup -VnetName -VnetResourceGroup -EnvName -LogAnalyticsWorkspaceName -AcrName -SqlServerFqdn
Short name used to derive the remaining resource names contoso -PrereqToken
PIM VNet range / Container Apps subnet (/27 minimum, /24 recommended) 10.50.0.0/22 / 10.50.0.0/24 -PrereqVnetAddressPrefix -PrereqSubnetAddressPrefix
Private-endpoint subnet (/26) 10.50.1.0/26 -PrivateEndpointSubnetName -PrivateEndpointSubnetAddressPrefix
Registry build pool pimbuild (tier S1, 1 agent) -AcrAgentPoolName (on New-PimHostingPrerequisites also -AcrAgentPoolTier -AcrAgentPoolCount)
Hub VNet: should the scripts peer to it? vnet-hub in rg-connectivity, <hub-subscription-id> -HubVnetName -HubVnetResourceGroup -HubVnetSubscriptionId, plus -SkipPeering if you create the peering yourself
Where the environment’s private DNS zone goes the hub’s group (default when a hub is given), or your own -PrivateDnsResourceGroup
Firewall allows only port 1433 to SQL? yes → Proxy -SqlConnectionPolicy Proxy
Who administers PIM / who may sign in pim.admin@contoso.com / group PIM-Admins -ManagerSuperAdmins -EasyAuthAllowedPrincipals (or -EasyAuthAllowAllTenantUsers: members only, never guests)
Mail sender / alert recipients pim@contoso.com / secops@contoso.com -MailSender -AlertRecipients
Edition Pro (updates from Invardia, ring 3) or Community Pro: -UpdateSource Invardia -UpdateRing 3. Community: omit both.

Rights: see Network options, Prerequisites. They are the same as for the guided install, plus Network Contributor on the hub if the scripts should create the peering.

What you need on the machine: PowerShell 7 (Windows PowerShell 5.1 also works) and the PIM Manager release package:

  • Pro / trial: Install-Invardia downloads the signed release package for your installation. Run the commands in this guide from that downloaded package folder (.\tools\setup\<Script>.ps1). They load the engine libraries next to them, so they do not work as single downloaded files.
  • Community: clone the public repository at the release tag and run the commands from its folder.
  • The one standalone script in this guide, New-PimHubPrepareScript.ps1 (Step 1.2), is also published on its own at https://invardia.com/support/pim/New-PimHubPrepareScript.ps1, so a hub owner can run it without the package.

Every script has its documentation page at https://invardia.com/docs/pim/scripts/<Script>/. See Network options, section 6, for the full list and the save, verify, read, run form for standalone scripts.

No Azure CLI and no PowerShell modules are needed for any command in this guide. The image is built through the registry’s own REST build API, on the build pool. The file the optional hub preparation script writes in Step 1.2 needs neither: the hub owner runs it with PowerShell 7 and a browser sign-in.


Step 1: Prepare the network (your team)

Do these before the install. None of them is done by PIM.

  1. Address space: reserve the PIM VNet range. It must not overlap the hub or anything peered to it.

  2. Hub peering. If the identity running the install will not get Network Contributor on the hub, write the hub owner a script:

    .\New-PimHubPrepareScript.ps1 -SpokeSubscriptionId <subscription-id> -SpokeResourceGroup rg-pim `
        -SpokeVnetName vnet-pim -HubResourceGroup rg-connectivity -HubVnetName vnet-hub `
        [-HubSubscriptionId <hub-subscription-id>] [-DeployPrincipalObjectId <object id of the installer>] [-DnsResourceGroup <dns-rg>]
    

    -HubSubscriptionId is optional: leave it out when the hub VNet is in the PIM subscription. The script only writes a file, prepare-hub-pim.ps1, which creates the hub side of the peering with the exact name the setup looks for. The hub owner runs that file with PowerShell 7 on any machine (no Azure CLI, no modules): first .\prepare-hub-pim.ps1 to see the plan, then .\prepare-hub-pim.ps1 -Apply (browser sign-in as the hub owner). Then install with -SkipPeering.

  3. Gateway transit / forwarded traffic. If admins come in over the hub’s VPN or ExpressRoute gateway, plan to turn on allow gateway transit (hub side) and use remote gateways (PIM side) after the install. If traffic goes through a hub firewall, plan allow forwarded traffic. The scripts create the peering with VNet access only.

  4. DNS forwarding: conditional forwarders from on-premises / custom DNS to your Azure DNS Private Resolver for the environment’s default domain (known after Step 3) and the privatelink zones (Network options, 1.3).

  5. Outbound path: a NAT gateway, or a route table to your firewall, with the allow-list from Network options, section 2.3. Include the build pool’s base-image hosts. The route table can be attached to the Container Apps subnet after Step 3 creates it, or you can create the subnet yourself first with the same name and range.

  6. Azure Policy: check the deny effects in Network options, section 5.2 (Premium registry, region, tags).

Verify: your hub owner confirms the hub side of the peering, if you used the prepare script. The DNS forwarders exist (they can point at the resolver now, and the zone comes later).


Step 2: Sign in

Two supported ways, with no client secret and no PowerShell modules:

  • Browser sign-in (you, at the keyboard). When a script needs a token and no session exists, PIM’s REST client opens the browser sign-in (authorization code with PKCE, never device code). Use the account that holds the rights above.
  • Invardia support session. If Invardia support runs the install for you through your tenant’s Invardia Support app, the scripts use that session automatically. Grant it first. See Support access in the guided-install documentation.

Pass -UseSignedInAccount. The signed-in person also becomes a member of the SQL admin group grp-pim-sql-admins. They need that to reach the database later.


Step 3: Plan the deployment (changes nothing)

Put the parameters in one place. Every name below is a real parameter of Invoke-PimDeployAll.ps1.

$p = @{
    Scenario                  = 'S2'                      # Single tenant
    TenantId                  = '<tenant-id>'
    SubscriptionId            = '<subscription-id>'
    Location                  = 'swedencentral'
    Exposure                  = 'internal'                # IMMUTABLE once the environment exists
    ResourceGroup             = 'rg-pim'
    VnetName                  = 'vnet-pim'
    VnetResourceGroup         = 'rg-pim'
    PrereqToken               = 'contoso'
    PrereqVnetAddressPrefix   = '10.50.0.0/22'
    PrereqSubnetAddressPrefix = '10.50.0.0/24'
    PrivateEndpointSubnetName          = 'pim-endpoints'
    PrivateEndpointSubnetAddressPrefix = '10.50.1.0/26'
    AcrName                   = 'acrpimcontoso'
    AcrSku                    = 'Premium'                 # the default for internal; stated for clarity
    AcrPublicAccess           = 'false'
    AcrAgentPoolName          = 'pimbuild'
    SqlServerFqdn             = 'sql-pim-contoso.database.windows.net'
    SqlDatabase               = 'PimPlatform'
    SqlConnectionString       = 'Server=tcp:sql-pim-contoso.database.windows.net,1433;Database=PimPlatform;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30'
    SqlPrivateEndpoint        = $true                     # the default for internal; stated for clarity
    SqlConnectionPolicy       = 'Proxy'                   # only if your firewall allows 1433 and nothing else
    EnvName                   = 'cae-pim'
    LogAnalyticsWorkspaceName = 'law-pim'
    LogAnalyticsResourceGroup = 'rg-pim'
    WorkerMode                = 'cron'
    HubVnetName               = 'vnet-hub'
    HubVnetResourceGroup      = 'rg-connectivity'
    HubVnetSubscriptionId     = '<hub-subscription-id>'
    # SkipPeering             = $true                     # if the hub owner made the peering (Step 1.2)
    # PrivateDnsResourceGroup = 'rg-dns'                  # optional, same subscription as PIM
    SkipHostedSmoke           = $true                     # the deploy host cannot reach a private PIM Manager; Step 6 verifies from inside
    ManagerSuperAdmins        = 'pim.admin@contoso.com'
    EasyAuthAllowedPrincipals = @('PIM-Admins')
    MailSender                = 'pim@contoso.com'
    AlertRecipients           = @('secops@contoso.com')
    UseSignedInAccount        = $true
    SkipAppReg                = $true                     # the hosted engine runs as a managed identity
    # Pro only:
    UpdateSource              = 'Invardia'
    UpdateRing                = 3
}
.\tools\setup\Invoke-PimDeployAll.ps1 @p

Without -Apply the run is plan only:

  • It checks the names, the region and the rights.
  • It warns if no hub is given (“the PIM spoke VNet will be left ISOLATED”).
  • It prints what the plan would change, per step.
  • It prints the resolved defaults, for example registry SKU: Premium (from -Exposure internal …) and sql: private endpoint ON (from -Exposure internal …).

Verify: the plan lists the steps prereq, image, infra, sqlaccess, schema, mailsender, features, easyauth, code, updater, access and alerting, with nothing refused.


Step 4: Build

.\tools\setup\Invoke-PimDeployAll.ps1 @p -Apply

What happens, in order. Each step is idempotent, so a failed run is fixed and re-run.

Step What it builds Private-mode detail
prereq (New-PimHostingPrerequisites.ps1) Resource group, VNet, delegated Container Apps subnet, Log Analytics, managed identities, registry, SQL server and database, SQL admin group Premium registry with public access off, its private endpoint in pim-endpoints, the privatelink.azurecr.io zone (both records), the build pool in the same subnet. SQL private endpoint and the privatelink.database.windows.net zone. A user-assigned SQL identity administers the database from inside the network.
image The PIM Manager image Built on the build pool, inside the VNet, through the registry’s REST build API (-AcrAgentPool)
infra Internal-only Container Apps environment, the PIM Manager (created closed: it answers 403 until sign-in is in place), the engine job ca-pim-tick, the bootstrap job ca-pim-dbinit Peering both ways (unless -SkipPeering), the private DNS zone for the default domain (@, *, *.internal → the static IP) linked to the PIM VNet and the hub, the Graph permissions and Azure roles for the managed identities
sqlaccess Subnet service endpoint and VNet rule on SQL Runs on every deploy
schema, features, access, alerting Database schema, feature switches, PIM Manager administrators, alert recipients Applied in-cloud by ca-pim-dbinit: this host has no route to private SQL
mailsender Notification mailbox and scoped send right Needs an active Exchange Administrator; otherwise it becomes a follow-up
easyauth The PIM Manager’s sign-in app, assignment required, then the closing restriction removed The reply address is the private address
code Rolls the apps and the engine job to the image digest —
updater Nightly updater ca-pim-update on your ring Gets PIM_ACR_AGENT_POOL=<pool>, so the nightly build also runs on the build pool. Community without a published source: skipped (see Step 8).

The run ends with the PIM Manager’s address and the static IP, and an Entra Private Access / private-link checklist.

Verify (from the deploy host):

  • the prereq === VERIFY === block ends with RESULT: OK;
  • the infra step shows env static IP = 10.50.0.x and Peering: … Connected on both sides;
  • the summary has no failed step.

The verify step reports unverified, not healthy, because the smoke test was skipped (-SkipHostedSmoke). That is expected. Step 6 verifies from inside.

Afterwards (your team):

  • attach the route table or NAT gateway to the Container Apps subnet, if you did not create the subnet yourself;
  • set gateway transit and forwarded traffic on the peering if needed;
  • point the conditional forwarders at the environment’s default domain, now printed.

Step 5: Licence and support access (Pro)

An enrollment key is refused when SQL is private: the licence step stores the licence from the deploy host, which has no route. Register the licence file from a machine inside the network, such as a jump host or the hub:

.\tools\setup\Set-PimLicense.ps1 -LicensePath .\Contoso.pimlicense -SqlServer sql-pim-contoso.database.windows.net `
    -TenantId <tenant-id> -UseSignedInAccount

This needs a member of grp-pim-sql-admins; the installer is one. To give your Invardia Support app access:

.\tools\setup\Grant-PimSupportAccess.ps1 -AppId <support-app-id> -Level troubleshoot -TenantId <tenant-id> `
    -SubscriptionId <subscription-id> -ResourceGroup rg-pim -SqlServer sql-pim-contoso.database.windows.net

-Level setup also makes the app a SQL admin and the owner of PIM’s own app registrations. Like the licence step, this one writes to SQL, so it must run from inside the network.


Step 6: Verify from inside the network

From a client in the hub, or reached through your VPN or Private Access:

Resolve-DnsName ca-pim-manager.<default-domain>              # -> 10.50.0.x (the static IP)
Resolve-DnsName sql-pim-contoso.database.windows.net         # -> CNAME privatelink... -> 10.50.1.x
Resolve-DnsName acrpimcontoso.azurecr.io                     # -> 10.50.1.x
Invoke-WebRequest https://ca-pim-manager.<default-domain>/ -MaximumRedirection 0 -SkipHttpErrorCheck |
    Select-Object StatusCode, @{n='Location';e={$_.Headers.Location}}   # 302 to login.microsoftonline.com -- never 200

From the internet, the name must not resolve to anything you can reach.

Then sign in to the PIM Manager as a SuperAdmin and open Overview. The engine’s first runs and the permission check should be green within one tick (5 minutes).

End-of-install check (repairs what it can), from inside the network:

.\tools\setup\Confirm-PimInstall.ps1 -Role Single -TenantId <tenant-id> -SubscriptionId <subscription-id> `
    -ResourceGroup rg-pim -SqlServerFqdn sql-pim-contoso.database.windows.net -EnvName cae-pim `
    -AcrName acrpimcontoso -AcrAgentPoolName pimbuild -SuperAdmins pim.admin@contoso.com -AlertRecipients secops@contoso.com

Confirm-PimInstall.ps1 runs over REST in your signed-in or support session, with no Azure CLI. It needs a route to the private SQL endpoint, which is why it runs from inside the network.


Step 7: Lock down (your team)

  1. Turn off SQL public network access. The scripts create the private endpoint but leave the server’s public endpoint on: it admits only the PIM subnet and, during a deploy, the setup host. Once Step 6 passes, set Public network access = Disabled on the SQL server, in the portal or with your Azure Policy. Not yet automated: no single-tenant script turns it off. After that, the deploy’s sqlaccess step reports not applicable on later deploys, which is expected.

  2. Check that the registry reports public network access Disabled. It was created that way.

  3. NSGs on the Container Apps subnet: inbound 443 from admin ranges only (Network options, 5.1).

  4. Optional: a custom name that survives rebuilds:

    .\tools\setup\Set-PimManagerCustomHost.ps1 -SubscriptionId <subscription-id> -TenantId <tenant-id> -ResourceGroup rg-pim `
        -HostName pim.contoso.local -KeyVaultName kv-contoso-certs -KeyVaultCertName pim-contoso-local `
        -Dns PrivateDnsZone -PrivateDnsResourceGroup rg-dns -LinkVnetIds <hub-vnet-id>,<pim-vnet-id> -WhatIf
    

    Remove -WhatIf to apply. On a private environment you must supply the certificate (Key Vault or -PfxPath/-PfxPassword). The sign-in reply address is added for you. The script writes the record only into a private zone contoso.local that already exists in rg-dns. It never creates a zone named after your domain, because that zone would answer for every name in it. If the zone is missing, it prints what to create. With -Dns AdDns, PIM prints the record for you to add to your AD DNS.


Step 8: How updates keep working privately

Edition How it updates What must stay open
Pro / trial The in-cloud updater ca-pim-update runs nightly (-UpdateCron, default 03:00 UTC). It reads Invardia’s signed manifest for its ring (-UpdateRing; the ring is set locally in your environment). It verifies the signature, builds the image on the build pool (PIM_ACR_AGENT_POOL), applies the schema in-cloud, rolls by digest, runs a health check and rolls back on failure. Outbound invardia.com only for the release itself: the signed manifest and the archive (https://invardia.com/api/updates/pim-manager/..., no separate storage host). Plus login.microsoftonline.com, management.azure.com, *.blob.core.windows.net (the build-context upload), SQL and the registry over their private endpoints. The build pool needs mcr.microsoft.com, *.data.mcr.microsoft.com and api.nuget.org.
Community No in-cloud updater. Run Update-PimCommunity.ps1 -Apply from the deploy host. It pulls the latest release (git pull --ff-only) and re-runs Invoke-PimDeployAll with the parameters your last successful -Apply saved, private parameters included. The profile is in %LOCALAPPDATA%\pim\deploy-profiles\. The deploy host needs git, github.com and objects.githubusercontent.com, and the REST endpoints (the image is built on the pool through REST). Schema changes are applied in-cloud.

Mirroring releases to your own storage is a support-led setup; ask Invardia support.

If the updater ever reports a failed build after you lock the registry, check that the job still carries PIM_ACR_AGENT_POOL. Re-running Invoke-PimDeployAll … -AcrAgentPoolName pimbuild -Apply restores it.


Rollback

Situation What to do
A step failed Fix the cause and re-run the same command. Every step checks what exists first.
Wrong exposure chosen The environment’s internal/external flag cannot be changed. To go public, use Rebuild-PimEnvExternal.ps1 (the reverse of the conversion guide).
Remove the whole installation Delete the resource group, the sign-in app registration (“PIM4EntraPS Manager”), and the group grp-pim-sql-admins. Then remove any peering and DNS records you created on the hub side.
Remove only the Container Apps part, keeping data Remove-PimContainerStack.ps1 -SubscriptionId -ResourceGroup [-Apply] deletes the jobs, the apps, the environment and their role assignments, and keeps SQL, the registry, the VNet and the identities. REST only, no Azure CLI.
InvardiaInvardia

See the risk. Control the access.

Products

PIM ManagerSecurityInsightEntra Policy SuiteAll featuresPricingFree 60-day Pro trialBook a demoWhy Invardia

Docs

DocumentationSearch the docsPIM Manager settingsSecurityInsight settingsPIM network guidesCommand wizardNetwork egressPIM Manager design configuratorSecurityInsight design configuratorDesign your installationCalculate your cost

Company

AboutContactCustomer portalSupportHow support worksHow updates reach youSupport in numbersSetup scriptsRemote support accessContact: info@invardia.com

Legal

Terms of saleLicence termsData processingPrivacyCookiesCompany information
© 2026 Invardia – a brand of 2LINKIT, founded and owned by Morten Knudsen · Strandvejen 21A, 6000 Kolding, Denmark · VAT DK39208032 · Contact: info@invardia.com