#Requires -Version 7.0 # PIM Manager 3.0.70 -- Invoke-PimSupportSqlAccessJob.ps1, standalone (built by tools/setup/Build-PimSupportScripts.ps1; helpers inlined: _pimscriptdoc.ps1, ..\..\engine\_shared\pim-rest.ps1, pim-loopbackanswer.ps1, _pimsignedin.ps1, _pimsupportsqlaccess.ps1, _pimsupportsqljob.ps1, ..\..\engine\_shared\pim-supportjit.ps1) <# .SYNOPSIS Gives the Invardia Support app its read-only database user on a PIM Manager installation whose database is private (public network access off), from inside: a one-time job in the installation's own environment runs the support access step (the user from the application's client id, in the read role pim_support_read only), the result is read back, and the job is removed. Nothing is opened; -WhatIf shows it first. With -SupportHours the database owner role is given for that many hours only (never above the level you consented to): the job stays, takes the role back at the end by itself, and the read-only access stays. .DESCRIPTION PIM REQUIREMENTS 100.116 (framework 4.1b). On an installation with a public database, Grant-PimSupportSqlAccess.ps1 does this from the machine it runs on. A PRIVATE database has no route from outside, and none is made for this: no firewall rule, no public access, no jump host. The grant runs inside the installation's own network instead, and you -- the customer -- start it. What it does, in order: 1. Reads the installation's own database setup job (ca-pim-dbinit, else caj-pim-dbinit): its environment, its image, the SQL server and database it names, and the user-assigned identity it signs in to the database with. That identity is the SQL server's administrator (or a member of the SQL admin group); it is used as it is. No role, no group membership and no permission is given to any identity. When the installation has no such job or identity, the script STOPS, changes nothing, and prints the options. 2. Reads whether a job of the one-time name is already there (a leftover of a killed run of this script is removed first; any other job of that name is not touched and stops the run), and that the Invardia Support app exists in this tenant. 3. Creates the one-time job (caj-pim-supportsql) in the same environment, with that identity and a tag that says what it is. 4. Starts it once. In the database, the job runs the support access step with the consent level (-ConsentLevel troubleshoot | setup; without it the level already recorded, else troubleshoot): the user from the Support app's client id when no user has that id (an existing user of the Support app is reused whatever its name), in pim_support_read only, then reads it back. It never adds the database owner role; one an earlier version gave is shown as a WARNING and left as found. 5. Waits for the execution, reads its result and its log lines, and prints PASS or FAIL with them. 6. ALWAYS, also after a failed step: removes the one-time job and reads back that it is gone. Time-limited access, in three steps: -InstallSupportAccess -ConsentLevel none|troubleshoot|setup ONCE per installation (an owner-level step): records the level you consent to, gives the Support app's database user read-only access, and installs the signed procedures that give and take back the time-limited access. They take no user as a parameter: the Support app and your consent level are read from a table only a database owner can change. -SupportHours 1-8 [-Level setup|troubleshoot] opens it: the level given is the LOWER of the level asked and the level you consented to; for the database owner role the end is recorded with the role. The job stays and takes the role back at the end by itself, then ends the Support app's open database sessions; the read-only access stays. Nothing is taken back before the end, and nothing else has to happen for the access to end. A role the Support app held before is never taken away. -CloseSupportAccess ends every open time-limited access now. Every run of the job first takes back any time-limited access whose end has passed, and so do the background jobs of PIM Manager on their own schedule; each of them reports what it took back. When the access is opened, the script waits for the job's result (read from its log lines) and leaves the job running; the next run of this script removes it. The step the job runs is the one Grant-PimSupportSqlAccess.ps1 runs: the job's script is built from the same functions. No SQL admin group membership is given by this script. Admin rights: consent 'setup' lets a support session (-SupportHours, at most 8) add the database owner role until its end; consent 'troubleshoot' is read-only. Outside a session the Support app reads only. Sign-in: your own browser sign-in (or the Invardia Support app's session open in this window). No Azure CLI, no PowerShell module, no secret on the command line. Every token is pinned to -TenantId. Exit codes: 0 PASS (or a preview with nothing in the way); 1 FAIL -- the reason and the next step are printed. .EXAMPLE .\Invoke-PimSupportSqlAccessJob.ps1 -TenantId -SubscriptionId -ResourceGroup rg-pim -SupportAppId -WhatIf Preview: reads everything, prints what it would do and the script the job would run. Then the same command without -WhatIf. .EXAMPLE .\Invoke-PimSupportSqlAccessJob.ps1 -TenantId -SubscriptionId -ResourceGroup rg-pim -SupportAppId -SupportHours 8 -TicketNumber 12345 -Reason 'database repair' Time-limited: the database owner role for 8 hours, taken back by the job at the end. .EXAMPLE .\Invoke-PimSupportSqlAccessJob.ps1 -TenantId -SubscriptionId -ResourceGroup rg-pim -SupportAppId -InstallSupportAccess -ConsentLevel owner Once per installation: records the consent level and installs the time-limited support access. .NOTES Requirements: PowerShell: 7.0 or later (pwsh). Windows PowerShell 5.1 is not supported. Role: Contributor (or Owner) on the resource group of the PIM Manager installation (Azure); Log Analytics Reader on the installation's Log Analytics workspace to show the job's log lines (optional). Hosts: login.microsoftonline.com, management.azure.com, graph.microsoft.com, api.loganalytics.io. Nothing to the SQL server from this computer: only the job, inside the installation's network, reaches the database. PERMISSIONS AND CHANGES (from Invoke-PimSupportSqlAccessJob.ps1 manifest, PIM Manager 3.0.70; full page: https://invardia.com/docs/pim/scripts/Invoke-PimSupportSqlAccessJob/) The person running it needs: - Contributor (or Owner) [azureRbac, the resource group of the PIM Manager installation] -- Reads the installation's database setup job and its environment; creates, starts and deletes the one-time job; and assigns to that job the user-assigned identities the setup job already carries (the assign action on those identities is part of Contributor). - Log Analytics Reader [azureRbac, the Log Analytics workspace of the installation's Container Apps environment (optional)] -- Reads the job's log lines. Without it the verdict still comes from how the execution ended; the script then says the log could not be read. - Your own browser sign-in, or the Invardia Support app's session in this window [other, the target tenant and subscription] -- A session of another tenant than -TenantId, or of another application than -SupportAppId, is refused. No Azure CLI, no PowerShell modules, no secret on the command line. What it creates, changes or grants: - grant a database user for the Invardia Support app, created by the one-time job from the application's client id (CREATE USER ... WITH SID, never FROM EXTERNAL PROVIDER) when no user has that id; an existing user of the Support app is reused whatever its name; made by the support access step with the consent level (-ConsentLevel troubleshoot or setup, else the level already recorded, else troubleshoot) (The PIM database): pim_support_read (read-only: SELECT on the product schemas) @ the one PIM database the installation's database setup job names (PIM_SqlDatabase, default PimPlatform) -- Invardia support reads the store to troubleshoot. This is the Support app's only standing database right. The job never adds db_owner; one an earlier version gave is shown as a warning and left as found. Undo: Run Grant-PimSupportAccess.ps1 -Remove from inside the network (drops the user), or switch the Invardia Support app off in Microsoft Entra ID, which ends the access at once. - grant with -SupportHours: the level asked (-Level setup or troubleshoot), but never above the level the customer consented to (the lower of the two); for the database owner role the end is recorded with the role (one record per role added). At the end the job takes the database owner role back by itself, then ends the Support app's open database sessions, and ends; the read-only access stays. A role the Support app held before is never taken away. Every run of the job, and the background jobs of PIM Manager, take back any time-limited access whose end has passed (The PIM database): db_owner (database owner) until the end, at most 8 hours; pim_support_read (read-only: SELECT on the product schemas) stays @ the one PIM database the installation's database setup job names (PIM_SqlDatabase, default PimPlatform) -- Support gets the owner right only while it works on the installation; the access ends at the stated time without anyone having to remember it. Undo: None needed: the job takes the owner role back at the end. To end it sooner: -CloseSupportAccess, or switch the Invardia Support app off in Microsoft Entra ID, which ends the access at once. - create with -InstallSupportAccess -ConsentLevel: once per installation, the consent level the customer gives (none, troubleshoot or setup) for this Support app, in a table only a database owner can change; the read role pim_support_read (SELECT on the product schemas only) and the Support app's database user in it (an earlier membership of db_datareader is taken away once it is in that role: narrower, never wider) -- with none, no Support database user at all; three procedures that open, close and report the time-limited access and take no user as a parameter, signed by a certificate created in the database (its private key removed right after signing), with changes to them refused to everyone but the database owner (The PIM database) @ the one PIM database the installation's database setup job names -- So the time-limited access can be opened and closed by PIM Manager's own jobs without them being database owners, and only ever for the Support app at the level the customer chose. Undo: Run -InstallSupportAccess again with -ConsentLevel none: every open access is closed and the Support app's database user is removed. - grant with -InstallSupportAccess: the right to run the procedures that close the time-limited access and report it, for PIM Manager's engine and web app (so their regular jobs end an access that has passed its end), and the right to read the state for the updater; only the database job may run the one that opens it (The PIM database): EXECUTE on those procedures only @ the three support access procedures in the PIM database -- PIM Manager's own jobs close the time-limited access on their own schedule without being database owners. Undo: Run -InstallSupportAccess again with -ConsentLevel none: nothing can be opened any more. - update with -CloseSupportAccess: every open time-limited access is ended now (only the roles it added), then the Support app's open database sessions (The PIM database) @ the one PIM database the installation's database setup job names -- Support is finished before the end that was set. Undo: Open it again with -SupportHours. - create the one-time Container Apps job (default name caj-pim-supportsql, tag pim-one-time-job = support-sql-access) in the installation's own Container Apps environment, on the image of the database setup job, with the user-assigned identities that job already carries (the SQL admin identity and, when it differs, the image-pull identity). No role, no group membership and no permission is given to any identity. (Azure (the PIM resource group)) @ the resource group of the installation -- A private database has no route from outside; the job runs where the route already is. No firewall rule, no public access and no network path is opened. Undo: None needed: the run removes the job and reads back that it is gone, also after a failed step. If the run is killed: Azure portal > the resource group > caj-pim-supportsql > Delete, or run the script again (it removes a leftover that carries its tag first). - remove the one-time job this run created -- or a leftover of the same name that carries this script's tag. A job of that name without the tag is never touched. (Azure (the PIM resource group)) @ the resource group of the installation -- The job is for one run. It carries the SQL admin identity, so it is not left behind. Undo: None needed: the installation does not use this job. -WhatIf: Signs in, reads the database setup job, whether a job of the one-time name exists and that the Support app exists in the tenant, prints the database, the identity and the image it would use, and one 'What if:' line for each thing it would do: create the one-time job, start it once (take back any time-limited access whose end has passed; the read-only user from the client id in pim_support_read only -- with -SupportHours, db_owner until the end), read the result, remove the job (with -SupportHours: leave it running until it has taken the access back). It also prints the whole script the job would run. No job is created, nothing reaches the database. Nothing is changed. Undo: Run Grant-PimSupportAccess.ps1 -Remove from inside the network (drops the database user), or switch the Invardia Support app off in Microsoft Entra ID. The one-time job is removed by the run itself. With -SupportHours the owner role ends at the stated time by itself. .LINK https://invardia.com/docs/pim/scripts/Invoke-PimSupportSqlAccessJob/ #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory)][ValidatePattern('^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$')][string]$TenantId, [Parameter(Mandatory)][ValidatePattern('^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$')][string]$SubscriptionId, # the resource group of the PIM Manager installation (its Container Apps environment and jobs) [Parameter(Mandatory)][ValidatePattern('^[A-Za-z0-9_.()-]{1,90}$')][string]$ResourceGroup, # the Invardia Support app's application (client) id. In the Support app's own session it may be left out. [ValidatePattern('^$|^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$')][string]$SupportAppId = '', [string]$DbUserName = 'invardia-support', # the installation's own job the identity, the image and the database are read from [ValidatePattern('^[a-z][a-z0-9-]{0,30}[a-z0-9]$')][string]$TemplateJobName = 'ca-pim-dbinit', # the name of the one-time job (created, run once, removed) [ValidatePattern('^[a-z][a-z0-9-]{0,30}[a-z0-9]$')][string]$JobName = 'caj-pim-supportsql', # only when the template job does not name its database identity itself: the client id of a user-assigned identity it already carries [ValidatePattern('^$|^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$')][string]$SqlIdentityClientId = '', [ValidateRange(60, 3600)][int]$TimeoutSeconds = 600, # time-limited access: the database owner role for this many hours (1-8), taken back by the job at the end; 0 = the standing access [ValidateRange(0, 8)][int]$SupportHours = 0, # with -SupportHours: the level asked (the level given is the lower of this and the consented level); v1 owner / read = setup / troubleshoot [ValidateSet('setup', 'troubleshoot', 'owner', 'read')][string]$Level = 'setup', # with -SupportHours: why the access is opened, and the support ticket it belongs to (recorded with the end) [ValidatePattern('^[A-Za-z0-9 ._@:#/()+,-]{0,120}$')][string]$Reason = '', [ValidatePattern('^[A-Za-z0-9._#-]{0,40}$')][string]$TicketNumber = '', # once per installation: record the consent level and install the time-limited support access [switch]$InstallSupportAccess, # with -InstallSupportAccess: none | troubleshoot | setup; without a step switch (the read-only user): troubleshoot | setup # ('' = the level already recorded, else troubleshoot) [ValidateSet('', 'none', 'troubleshoot', 'setup', 'read', 'owner')][string]$ConsentLevel = '', # v1 read / owner = troubleshoot / setup # end every open time-limited support access now [switch]$CloseSupportAccess, # --- test seams (tests/Test-PimSupportSqlJob.ps1); omit them for a real run --- [scriptblock]$Arm, [scriptblock]$Graph, [scriptblock]$Logs, [scriptblock]$Sleep, [int]$PollSeconds = 10, [int]$LogWaitSeconds = 180 ) $ErrorActionPreference = 'Stop' # Published as ONE standalone file (https://invardia.com/support/pim/Invoke-PimSupportSqlAccessJob.ps1): every helper is # dot-sourced by a literal path relative to this script's folder, so Build-PimSupportScripts.ps1 inlines it. # ===== BEGIN inlined helper: _PimScriptDoc.ps1 ===== <# .SYNOPSIS Framework 12.7 SCRIPT-DOC-1 + 12.10 item 11 -- the ONE place that knows where a PIM script's documentation page, download and checksum list live, the command form every surface shows (line 1 download, line 2+ run -- owner 2026-10-11), and the run frame every script a person runs uses (the first line "