Security review pack
Entra Policy Suite - Security review pack
| Product | Entra Policy Suite |
|---|---|
| Version | 2.0.107 |
| Date | |
| Published at | invardia.com/products/entrapolicysuite/security/ |
| Questions | info@invardia.com |
This pack is for the CISO and the security team who must approve Entra Policy Suite before it is installed. It describes the product and this version, not one customer's environment. It sets out Entra Policy Suite's security design: architecture, identities and every permission, data, vendor access, supply chain, change control, the threat model, the compliance mapping, exit and the scripts a person runs. The facts in it are read from the code of this release.
1. Architecture
Entra Policy Suite gives every user and device the tags your Conditional Access policies and dynamic groups depend on, from rules you control. It watches those tags for changes it did not make, reports the accounts that need attention (identity report), and documents and manages Conditional Access. It runs entirely in your own Azure subscription, in one resource group you choose. Invardia does not host the product, your data or its runtime.
Components (all in your subscription)
- Scheduler - the engine's Container Apps job, run every 5 minutes as the engine identity. It works out which job is due and asks for it by putting a message on that job's own queue.
- Work jobs - Container Apps jobs, each started by a message on its own queue. They run the same container image and differ only by the job they are told to run.
- Web app - one Container App with two containers: the shared sign-in front (version 1.4.5, pinned by this release) on the HTTPS ingress, and the web app itself on the container's loopback address behind it. People sign in with Microsoft Entra ID.
- Azure SQL Database - every rule, setting, run, tag change, snapshot and audit row. Microsoft Entra authentication only.
- Storage account - one queue per work job, through which the scheduler asks for runs. Shared keys are off.
- Container registry - the engine image and the sign-in front image, built inside your registry from the release.
- Key Vault - one key that protects the web app's sign-in sessions; and, when Invardia has issued one, the per-install key of the Invardia uplink.
- Log Analytics - the container logs.
- Hybrid worker (optional) - a scheduled task on a domain-joined Windows Server that writes the tags of AD-synced users in Active Directory and reads AD users and groups. It connects out to the database; nothing connects in to it.
How a change flows
- A person changes a rule, a setting or a schedule in the web app, or asks for a run. The web app records the request with the person's identity and reason. It cannot change the directory or the configuration itself.
- The engine picks up the request, verifies the person's sign-in proof and checks their role at that moment, then carries it out.
- Every tagging run builds a plan first. In Preview it records the plan only; in Apply it writes it. A run whose plan is far larger than usual is held for an admin's approval.
- Every step is written to the run log and the audit trail.
Network
- Public install (default): the web app has a public HTTPS endpoint behind Microsoft Entra sign-in. The jobs reach Microsoft Graph, Exchange Online, the database, the queues and the Key Vault over HTTPS (TLS 1.2 or later).
- Private install (option): the install puts everything in your virtual network. Azure SQL, the Key Vault, the job queues and the registry are created with public network access off and reached through private endpoints; the Container Apps environment is internal (no public address). The install proves the result from inside the network before it reports success, and reads public access back as Disabled at the end.
- Private DNS zones a private install uses:
privatelink.database.windows.net,privatelink.vaultcore.azure.net,privatelink.queue.core.windows.net,privatelink.azurecr.io. - Outbound hosts a private install reaches (your firewall decides): graph.microsoft.com, login.microsoftonline.com, outlook.office365.com, management.azure.com, invardia.com, mcr.microsoft.com (the base images of the build).
| Job | Default schedule | How it starts | Identity | Resources | Longest run |
|---|---|---|---|---|---|
| Support read (one named read when the Support app asks) | on request | A message on its own queue; at most 1 at a time | Support read job | 0.25 CPU, 0.5Gi | 0.2 h |
| Scheduler | every 5m | On a schedule (*/5 * * * *, every 5 minutes) | Engine | 0.25 CPU, 0.5Gi | 0.5 h |
| User tagging | daily 01:00 | A message on its own queue; at most 1 at a time | Engine | 2 CPU, 4Gi | 168 h |
| Device tagging | daily 01:30 | A message on its own queue; at most 1 at a time | Engine | 2 CPU, 4Gi | 168 h |
| Identity report | daily 02:00 | A message on its own queue; at most 1 at a time | Engine | 2 CPU, 4Gi | 168 h |
| Named locations from Azure service tags (only the Azure.Network.ServiceTag.* locations) | daily 06:00 | A message on its own queue; at most 1 at a time | Engine | 2 CPU, 4Gi | 168 h |
| Tamper monitor | every 15m | A message on its own queue; at most 1 at a time | Engine | 2 CPU, 4Gi | 1 h |
| CA Policy Analyzer | manual | A message on its own queue; at most 10 at a time | Engine | 2 CPU, 4Gi | 168 h |
| Configuration (applies the changes people ask for in the web app) | manual | A message on its own queue; at most 1 at a time | Engine | 2 CPU, 4Gi | 168 h |
| Invardia uplink | every 4h | A message on its own queue; at most 1 at a time | Engine | 2 CPU, 4Gi | 168 h |
| Update check (which version is approved for this install - it installs nothing in this release) | daily 03:00 | A message on its own queue; at most 1 at a time | Engine | 2 CPU, 4Gi | 168 h |
| Backup (every night: the Conditional Access policies and the tagging configuration - it only reads the tenant) | daily 00:30 | A message on its own queue; at most 1 at a time | Engine | 2 CPU, 4Gi | 168 h |
| Policy options from Microsoft Graph (monthly: the values each Conditional Access field allows and the tenant's strengths, locations and roles - it only reads) | monthly 1 04:00 | A message on its own queue; at most 1 at a time | Engine | 2 CPU, 4Gi | 168 h |
| # | Step |
|---|---|
| 1 | Resource group |
| 2 | Managed identities (engine, web, the database setup job) |
| 3 | Log Analytics workspace |
| 4 | Container registry (Basic) + AcrPull for the identities |
| 5 | The engine image, built in the registry |
| 6 | The SQL admin group (grp-eps-sql-admins) with the database setup job's identity in it |
| 7 | Azure SQL (Entra-only) + database |
| 8 | Container Apps environment |
| 9 | The database, set up by the database setup job as its own identity: schema, one least-privilege role per identity, the sign-in front's state, you as the first SuperAdmin |
| 10 | The job queues (storage, no shared key): the tick may only send, the engine read |
| 11 | The web sign-in registration (no secret; assignment required; you assigned) |
| 12 | The tick + work jobs |
| 13 | The tick may NOT start jobs (an older install's job-starter role removed) |
| 14 | The engine's Graph permissions |
| 15 | The engine's Exchange permission + role |
| 16 | Key Vault for the sign-in front's wrap key |
| 17 | The sign-in front image (the pinned version), built in the registry |
| 18 | The web app: the sign-in front + the GUI in one revision |
| 19 | Verify: read everything back, run the tick once |
| 20 | An older install's separate scheduler identity removed - the scheduler runs as the engine |
| # | Step |
|---|---|
| 1 | Resource group |
| 2 | Network: the spoke, the apps subnet (Container Apps) and the endpoints subnet |
| 3 | Private DNS zones (SQL, Key Vault, queues, registry), linked to the spoke |
| 4 | Managed identities (engine, web, the database setup job) |
| 5 | Log Analytics workspace |
| 6 | Container registry (Premium, public access off) + AcrPull for the identities |
| 7 | Private endpoint + DNS records: the registry (login server and data endpoint) |
| 8 | The SQL admin group (grp-eps-sql-admins) with the database setup job's identity in it |
| 9 | Azure SQL (Entra-only, public access off) + database |
| 10 | Private endpoint + DNS record: Azure SQL |
| 11 | The job queues (storage, no shared key, public access off) |
| 12 | Private endpoint + DNS record: the job queues |
| 13 | The web sign-in registration (no secret; assignment required; you assigned) |
| 14 | Key Vault for the sign-in front's wrap key (public access off) |
| 15 | Private endpoint + DNS record: Key Vault |
| 16 | Container Apps environment inside the network (internal: no public address) |
| 17 | The environment's own DNS zone (@, *, *.internal), linked to the spoke |
| 18 | Hub peering / zone links to the hub (only with -PeerToHub / -LinkZonesToHub) |
| 19 | The network hand-off list (and the hub owner's script) |
| 20 | DNS check from this machine: every private name resolves to its endpoint |
| 21 | The registry's build pool inside the network |
| 22 | The engine image, built in the registry |
| 23 | The database, set up by the database setup job as its own identity: schema, one least-privilege role per identity, the sign-in front's state, you as the first SuperAdmin |
| 24 | The tick + work jobs |
| 25 | The tick may NOT start jobs (an older install's job-starter role removed) |
| 26 | The engine's Graph permissions |
| 27 | The engine's Exchange permission + role |
| 28 | The sign-in front image (the pinned version), built in the registry |
| 29 | The web app: the sign-in front + the GUI in one revision |
| 30 | Verify: read everything back, run the tick once |
| 31 | Inside check: from inside the network - private DNS, the web app, every job queue |
| 32 | Public -> private: the old public environment removed (after the inside check) |
| 33 | An older install's separate scheduler identity removed - the scheduler runs as the engine |
| 34 | Lockdown: public access read back as Disabled on SQL, Key Vault, storage and the registry |
2. Identities
Every identity the product creates or uses, with its purpose. The runtime identities are managed identities: there is no client secret and no certificate to create, store or rotate. Each part has its own identity and only the rights of chapter 3.
People in the web app
People sign in with their Entra account to the web app's own registration (single tenant, assignment required). A sign-in session lasts at most 8 hours. In Entra Policy Suite each person has one of 3 roles; the roles are kept in the database and checked on every request. A role is bound to the person's account, so a new account with the same name does not inherit it. Every grant and removal of access is in the audit trail.
| Identity | Kind | Purpose | Credential | Lifetime |
|---|---|---|---|---|
| Engine | User-assigned managed identity | Runs the scheduler and every work job: works out every 5 minutes which job is due, reads the tenant, writes the tags, applies the changes people ask for in the web app, sends alert mail and the Invardia uplink | None (managed identity) | As long as the install's resource group |
| Web app | User-assigned managed identity | Runs the web app and its sign-in front; it is also the client credential of the web sign-in registration (a federated credential). It holds no Microsoft Graph, Exchange or Azure management right | None (managed identity) | As long as the install's resource group |
| Database setup job | User-assigned managed identity | Runs only the database setup job, started by the install, and the support-access job every 5 minutes: the schema, one role per identity and their database users, and the Support app's database access at the level your organization set in the Invardia customer portal (none; read; or read plus admin only during an open support session of at most 8 hours). It is the only member the install puts in the SQL admin group grp-eps-sql-admins, so no person needs database admin rights. It holds no Microsoft Graph, Exchange or Azure management right | None (managed identity) | As long as the install's resource group |
| Updater | User-assigned managed identity | Used only by the update job, to install a release that Invardia approved for your update ring: it builds the new image in your own container registry, runs the database setup job and moves the jobs and the web app to the new image. On every run it also keeps the database setup identity a member of the SQL admin group and renews that membership before it ends. It holds Contributor on the install's resource group, four Microsoft Graph rights for that one group membership (chapter 3), and no Exchange or database right | None (managed identity) | As long as the install's resource group |
| Support read job | User-assigned managed identity | Runs only the support read job: when Invardia's Support app asks (a message on its own queue) it runs ONE read from the list this release ships - recent runs, job schedules, settings, the license state (never its text), the hybrid workers, the update runs - writes the result to its own log and records it in the audit. It reads only those tables and writes only the audit | None (managed identity) | As long as the install's resource group |
| Hybrid worker | The managed identity of the worker machine (Azure VM or Azure Arc-enabled server) | Signs in to the database to take its own work and report it back | None (managed identity) | As long as the worker machine |
| Web sign-in registration | App registration + enterprise application, single tenant | People sign in to the web app with it. Assignment is required: only people and groups you assign can sign in | None: no client secret and no certificate (the web app identity is its federated credential) | Until you delete it |
| Hybrid worker (Active Directory) | Group managed service account (gMSA) you name | Writes the tag attributes of AD-synced users, only in the OUs you delegate | Managed by Active Directory (no password to handle) | As long as you keep it |
| The person who installs | Your own Entra account, signed in in a browser | Creates the resources and grants listed in chapter 3. Becomes the first SuperAdmin of the web app | Your own sign-in; the installer stores nothing | Only while the install runs |
| Role | Can |
|---|---|
| Reader | Sees every page, the run logs and the audit trail. Changes nothing. |
| Admin | Edits rules, tag types, conditions, scopes and settings; asks for runs; commits the commit queue; accepts or restores tamper events. |
| SuperAdmin | Everything an Admin can, plus the access list, the protection settings and the Conditional Access stage moves. The last SuperAdmin cannot be removed. |
3. Permissions with justification
Every permission the product grants or requires, per identity, read from the install code of this version. Azure rights are given to managed identities only, at the narrowest scope Azure offers: a single queue, a single Key Vault key, a single Key Vault item, a single mailbox.
Least-privilege choices made in code
- The web app holds no Microsoft Graph, Exchange or Azure management right. It records requests; the engine carries them out after it checks the person's role.
- The scheduler may only put a message on a job's queue. It cannot start a job, change what a job runs, or read the queues.
- The engine's Graph rights are read only except the tag writes on users and devices, and the one Conditional Access stage move a SuperAdmin asks for.
- Any of the engine's Graph permissions can be left out at install (
-ExcludeGraphRole). A permission that is left out is never granted, and the feature that needs it reports its data as unavailable. - Mail is sent from one shared mailbox through Exchange role-based access for applications - never a tenant-wide right to send mail.
- In the database, each part has its own role with object-level grants on the tables it uses. No part is owner, reader or writer of everything (no db_owner, db_datareader or db_datawriter); a re-run of the install takes such a membership away. The audit trail is INSERT and SELECT only for every role.
- The install converges: a re-run grants what is listed and revokes any other table permission a role holds.
| Identity | Type | Permission | Scope | Why it is needed | Access | Can be left out |
|---|---|---|---|---|---|---|
| Engine | Microsoft Graph application | User.Read.All | Tenant | Read users for the rules, the identity report, the CA Policy Analyzer and the tamper monitor | Read only | Yes: the feature that needs it reports its data as unavailable |
| Engine | Microsoft Graph application | Device.Read.All | Tenant | Read devices for the rules and the identity report | Read only | Yes: the feature that needs it reports its data as unavailable |
| Engine | Microsoft Graph application | GroupMember.Read.All | Tenant | Read group members for the rule conditions and the identity report | Read only | Yes: the feature that needs it reports its data as unavailable |
| Engine | Microsoft Graph application | Organization.Read.All | Tenant | Read the tenant's license plans for the license conditions and the identity report | Read only | Yes: the feature that needs it reports its data as unavailable |
| Engine | Microsoft Graph application | AuditLog.Read.All | Tenant | Read the directory audit log to name who changed a watched tag (tamper monitor), and the last sign-in time of users (identity report) | Read only | Yes: the feature that needs it reports its data as unavailable |
| Engine | Microsoft Graph application | Policy.Read.All | Tenant | Read Conditional Access policies and named locations (Conditional Access page, CA Policy Analyzer) | Read only | Yes: the feature that needs it reports its data as unavailable |
| Engine | Microsoft Graph application | RoleManagement.Read.Directory | Tenant | Read directory role assignments (CA Policy Analyzer, the identity report's privileged accounts) | Read only | Yes: the feature that needs it reports its data as unavailable |
| Engine | Microsoft Graph application | DeviceManagementManagedDevices.Read.All | Tenant | Read Intune managed devices for the identity report | Read only | Yes: the feature that needs it reports its data as unavailable |
| Engine | Microsoft Graph application | Application.Read.All | Tenant | Read application names for the Conditional Access documentation | Read only | Yes: the feature that needs it reports its data as unavailable |
| Engine | Microsoft Graph application | Policy.ReadWrite.ConditionalAccess | Tenant | Change one Conditional Access policy (a stage move, an edit, a copy, a template deployed or a version promoted) when a SuperAdmin asks for it, after the change is shown to them; create the denied-countries and trusted named locations on request; and, when switched on, keep the Azure.Network.ServiceTag.* named locations up to date daily. Only the engine makes these writes | Write (on request; the service-tag locations daily) | Yes: the feature that needs it reports its data as unavailable |
| Engine | Microsoft Graph application | Group.ReadWrite.All | Tenant | Create the groups the Conditional Access policies use, set a dynamic pilot group's rule for the policy's stage, and change the members of the pilot and exclude groups - only when a SuperAdmin asks for it, after the change is shown to them, and only for the groups Entra Policy Suite manages | Write (its own groups, on request) | Yes: the feature that needs it reports its data as unavailable |
| Engine | Microsoft Graph application | AdministrativeUnit.ReadWrite.All | Tenant | Create the three administrative units of the Conditional Access groups and place those groups in them, when a SuperAdmin asks for it | Write (on request) | Yes: the feature that needs it reports its data as unavailable |
| Engine | Microsoft Graph application | Policy.ReadWrite.AuthenticationMethod | Tenant | Create the two authentication strengths v1 creates (FIDO2 + Temporary Access Pass with 11 named key models, Windows Hello for Business + Temporary Access Pass), when a SuperAdmin asks for it; an existing strength is never changed | Write (on request) | Yes: the feature that needs it reports its data as unavailable |
| Engine | Microsoft Graph application | User.ReadWrite.All | Tenant | Write the tag attributes of your tag types on users (user tagging, the tamper monitor's restore); with Pro, disable (and enable again) a stale account a SuperAdmin approved | Write | Yes: only while user tagging, the tamper monitor or (Pro) the stale identity clean-up's disable action (ProStale.DisableEnabled) is on - the install removes it otherwise |
| Engine | Microsoft Graph application | Device.ReadWrite.All | Tenant | Write the tag attributes of your tag types on devices (device tagging) | Write | Yes: only while device tagging is on - the install removes it otherwise |
| Updater | Microsoft Graph application | PrivilegedAssignmentSchedule.ReadWrite.AzureADGroup | Tenant (used for the SQL admin group only) | Keep the database setup identity a member of the SQL admin group (a role-assignable group) through a Privileged Identity Management for Groups assignment, and renew a time-limited one before it ends. It can change who is a member of role-assignable groups, so it is a highly privileged right; Entra Policy Suite uses it only for that one membership | Write (one membership) | No: the SQL admin group is role-assignable, so only these rights can keep the database setup identity in it |
| Updater | Microsoft Graph application | PrivilegedAssignmentSchedule.Read.AzureADGroup | Tenant (used for the SQL admin group only) | Read that assignment back, and when it ends | Read only | No: the SQL admin group is role-assignable, so only these rights can keep the database setup identity in it |
| Updater | Microsoft Graph application | RoleManagementPolicy.Read.AzureADGroup | Tenant (used for the SQL admin group only) | Read the SQL admin group's Privileged Identity Management policy: how long an assignment may last | Read only | No: the SQL admin group is role-assignable, so only these rights can keep the database setup identity in it |
| Updater | Microsoft Graph application | GroupMember.Read.All | Tenant (used for the SQL admin group only) | Read the membership back after it is made or renewed (the update job checks that the database setup identity is in the group) | Read only | No: the SQL admin group is role-assignable, so only these rights can keep the database setup identity in it |
| Updater | Microsoft Graph application | Application.Read.All | Tenant (used for the SQL admin group only) | Read the database setup identity (a service principal) and its group memberships back from its own side (memberOf) | Read only | No: the SQL admin group is role-assignable, so only these rights can keep the database setup identity in it |
| Updater | Microsoft Graph application | Application.ReadWrite.OwnedBy | Tenant (used for the SQL admin group only) | Keep the web app's own sign-in registration as it should be (its address, sign-in addresses, the web identity's credential): ONLY the registration it owns - no other app in the tenant | Only what it owns | No: the SQL admin group is role-assignable, so only these rights can keep the database setup identity in it |
| Engine | Exchange Online application | Exchange.ManageAsApp | Tenant | Read recipient types for the Exchange rule conditions; write a mail-enabled user's custom attribute where Graph does not | Read, and the custom attributes | Yes: only while user tagging, the identity report or a rule with a mailbox condition is on (the install grants or removes it) |
| Engine | Entra directory role | Exchange Recipient Administrator | Tenant | Needed with Exchange.ManageAsApp for the Exchange calls above | Recipients | Yes: only while user tagging, the identity report or a rule with a mailbox condition is on (the install grants or removes it) |
| Engine | Exchange Online (RBAC for applications) | Application Mail.Send | The one sender mailbox only | Send alert mail from that mailbox. No tenant-wide right to send mail is granted; a tenant-wide Graph Mail.Send on the identity is removed | Send as one mailbox | Yes: set up only when you run Initialize-EpsMailSender.ps1 |
| Engine | Azure role | AcrPull | The container registry | Pull the engine image | Read only | No |
| Web app | Azure role | AcrPull | The container registry | Pull the web app's images | Read only | No |
| Database setup job | Azure role | AcrPull | The container registry | Pull the engine image for the database setup job | Read only | No |
| Database setup job | Azure role | Key Vault Secrets User | The one Key Vault item 'uplink-key' only, and only when Invardia has put it there | Ask Invardia for the support access level your organization set (the support-access job) | Read only | No |
| Updater | Azure role | AcrPull | The container registry | Pull the engine image for the update job | Read only | No |
| Updater | Azure role | Contributor | The install's resource group only | Install a release Invardia approved for this ring: build the image in the registry, run the database setup job, move the jobs and the web app to the new image | Use | No |
| Engine | Azure role | Storage Queue Data Message Sender | Each job queue | The scheduler (part of the engine job, as the engine identity) asks for a job run by putting a message on that job's queue | Use | No |
| Engine | Azure role | Storage Queue Data Message Processor | Each job queue | A job takes and deletes its own message | Use | No |
| Engine | Azure role | Storage Queue Data Reader | Each job queue | The queue trigger counts the messages as the engine identity | Read only | No |
| Engine | Azure role | Key Vault Secrets User | The one Key Vault item 'uplink-key' only, and only when Invardia has put it there | Read the per-install key for the Invardia uplink at send time | Read only | No |
| Support read job | Azure role | AcrPull | The container registry | Pull the engine image for the support read job | Read only | No |
| Support read job | Azure role | Storage Queue Data Message Processor | Its own queue only | Take and delete the one message that names a support read | Use | No |
| Support read job | Azure role | Storage Queue Data Reader | Its own queue only | The queue trigger counts its messages | Read only | No |
| Invardia's Support app | Azure role | Storage Queue Data Message Sender | The support read job's queue only, and only when you gave the install its id | Ask for ONE named read from the list this release ships - no other right on the job, the database or the network | Use | No |
| Engine | Azure role | Reader | The web app's container app only | Report to Invardia which version of the sign-in front the web app runs (read only; owner 2026-11-06) | Read only | No |
| Web app | Azure role | Key Vault Crypto Service Encryption User | The one Key Vault key 'signin-front' only | Wrap and unwrap the keys that protect sign-in sessions | Use | No |
| Web sign-in registration | Microsoft Graph delegated (admin consent) | openid, profile, offline_access, User.Read | The signed-in person | Sign the person in and keep the session fresh | The person's own profile | No |
| Web sign-in registration | Its own delegated scope | user_impersonation | The web app only | The web app checks that the person's sign-in was made for it | The web app | No |
| Engine | Database role | eps_engine | The Entra Policy Suite database | Object-level grants on the tables this part uses, nothing more (table below) | 69 tables | No |
| Support read job | Database role | eps_supportread | The Entra Policy Suite database | Object-level grants on the tables this part uses, nothing more (table below) | 6 tables | No |
| Engine | Database role | eps_tick | The Entra Policy Suite database | Object-level grants on the tables this part uses, nothing more (table below) | 6 tables | No |
| Web app | Database role | eps_web | The Entra Policy Suite database | Object-level grants on the tables this part uses, nothing more (table below) | 61 tables | No |
| Hybrid worker | Database role | eps_worker | The Entra Policy Suite database | Object-level grants on the tables this part uses, nothing more (table below) | 8 tables | No |
| Hybrid worker (gMSA) | Active Directory | WriteProperty on each named extensionAttribute, inherited by descendant user objects | each OU named | Write the tag attributes of AD-synced users | Write, named attributes only | Yes: only with the hybrid worker |
| Role | Used by | Table | Permissions |
|---|---|---|---|
| eps_engine | Engine | AdGroupMember | SELECT |
| eps_engine | Engine | AdGroupSnapshotInfo | SELECT, INSERT, UPDATE |
| eps_engine | Engine | AdminUnit | SELECT, INSERT, UPDATE |
| eps_engine | Engine | AdUserSnapshot | SELECT |
| eps_engine | Engine | AdUserSnapshotInfo | SELECT, INSERT, UPDATE |
| eps_engine | Engine | AuditEvent | SELECT, INSERT |
| eps_engine | Engine | BreakGlassAccount | SELECT, INSERT, UPDATE |
| eps_engine | Engine | CaAdoption | SELECT, INSERT, UPDATE |
| eps_engine | Engine | CaAnalysis | SELECT, INSERT |
| eps_engine | Engine | CaAnalysisResult | SELECT, INSERT |
| eps_engine | Engine | CaBackup | SELECT, INSERT, DELETE |
| eps_engine | Engine | CaBackupItem | SELECT, INSERT, DELETE |
| eps_engine | Engine | CaDirectoryName | SELECT, INSERT, DELETE |
| eps_engine | Engine | CaFinding | SELECT, INSERT, UPDATE |
| eps_engine | Engine | CaGraphOption | SELECT, INSERT, UPDATE, DELETE |
| eps_engine | Engine | CaMonitor | SELECT, INSERT, UPDATE, DELETE |
| eps_engine | Engine | CaPersona | SELECT, INSERT, UPDATE, DELETE |
| eps_engine | Engine | CaPolicy | SELECT, INSERT, DELETE |
| eps_engine | Engine | CaStageMove | SELECT, INSERT |
| eps_engine | Engine | CaTemplateTaken | SELECT, INSERT, UPDATE |
| eps_engine | Engine | CaVersionPair | SELECT, INSERT, UPDATE |
| eps_engine | Engine | ConditionType | SELECT, INSERT, UPDATE, DELETE |
| eps_engine | Engine | ConfigBackup | SELECT, INSERT, DELETE |
| eps_engine | Engine | ConfigBackupItem | SELECT, INSERT, DELETE |
| eps_engine | Engine | ConfigChange | SELECT, INSERT |
| eps_engine | Engine | DeviceTagRule | SELECT, INSERT, UPDATE, DELETE |
| eps_engine | Engine | DeviceTagRuleCondition | SELECT, INSERT, DELETE |
| eps_engine | Engine | GroupPin | SELECT, INSERT |
| eps_engine | Engine | GroupRuleOverride | SELECT, INSERT, DELETE |
| eps_engine | Engine | IdentityAction | SELECT, INSERT, UPDATE, DELETE |
| eps_engine | Engine | IdentityLaRun | SELECT, INSERT |
| eps_engine | Engine | IdentityRow | SELECT, INSERT |
| eps_engine | Engine | IdentitySnapshot | SELECT, INSERT, DELETE |
| eps_engine | Engine | IdentityViewMember | SELECT, INSERT |
| eps_engine | Engine | JobRequest | SELECT, INSERT, UPDATE(Status, StatusText, RunId, UpdatedAtUtc, the request content) |
| eps_engine | Engine | JobSchedule | SELECT, INSERT, UPDATE |
| eps_engine | Engine | License | SELECT, INSERT, UPDATE |
| eps_engine | Engine | LicensePull | SELECT, INSERT, UPDATE |
| eps_engine | Engine | ManagedGroup | SELECT, INSERT, DELETE |
| eps_engine | Engine | MonitorState | SELECT, INSERT, UPDATE |
| eps_engine | Engine | OnboardingCheck | SELECT, INSERT, DELETE |
| eps_engine | Engine | PendingChange | SELECT, UPDATE |
| eps_engine | Engine | ProLicensePrice | SELECT |
| eps_engine | Engine | ProPasskeyProgress | SELECT, INSERT, UPDATE, DELETE |
| eps_engine | Engine | ProPasskeyTarget | SELECT, UPDATE |
| eps_engine | Engine | ProPolicyCodeSync | SELECT, INSERT |
| eps_engine | Engine | ProStaleDecision | SELECT, UPDATE |
| eps_engine | Engine | RefreshChangedStats | EXECUTE |
| eps_engine | Engine | Run | SELECT, INSERT, UPDATE |
| eps_engine | Engine | RunLog | SELECT, INSERT, DELETE |
| eps_engine | Engine | Scope | SELECT, INSERT, UPDATE, DELETE |
| eps_engine | Engine | ServiceTagLocation | SELECT, INSERT |
| eps_engine | Engine | ServiceTagPrefix | SELECT, INSERT, DELETE |
| eps_engine | Engine | Setting | SELECT, INSERT, UPDATE |
| eps_engine | Engine | SupportAccess | SELECT |
| eps_engine | Engine | SupportEvent | SELECT, INSERT, UPDATE |
| eps_engine | Engine | TagChange | SELECT, INSERT |
| eps_engine | Engine | TagRuleHit | SELECT, INSERT, DELETE |
| eps_engine | Engine | TagType | SELECT, INSERT, UPDATE, DELETE |
| eps_engine | Engine | TagValue | SELECT, INSERT, UPDATE, DELETE |
| eps_engine | Engine | TamperEvent | SELECT, INSERT, UPDATE |
| eps_engine | Engine | TargetGroup | SELECT, INSERT, UPDATE |
| eps_engine | Engine | UplinkState | SELECT, INSERT, UPDATE |
| eps_engine | Engine | UserTagRule | SELECT, INSERT, UPDATE, DELETE |
| eps_engine | Engine | UserTagRuleCondition | SELECT, INSERT, DELETE |
| eps_engine | Engine | WatchedValue | SELECT, INSERT, UPDATE |
| eps_engine | Engine | WebAccess | SELECT, INSERT, UPDATE, DELETE |
| eps_engine | Engine | WorkerHeartbeat | SELECT |
| eps_engine | Engine | WorkPackage | SELECT, INSERT |
| eps_supportread | Support read job | AuditEvent | SELECT, INSERT |
| eps_supportread | Support read job | JobSchedule | SELECT |
| eps_supportread | Support read job | License | SELECT(Id, ClaimAction, ClaimMessage, ClaimAtUtc, RequestStatus, RequestAtUtc, NextPollUtc, SupersededBy, UsersInScope, UsageState) |
| eps_supportread | Support read job | Run | SELECT, INSERT |
| eps_supportread | Support read job | Setting | SELECT |
| eps_supportread | Support read job | WorkerHeartbeat | SELECT |
| eps_tick | Engine | CaMonitor | SELECT, UPDATE |
| eps_tick | Engine | JobRequest | SELECT(RequestId, Job, Mode, RequestedBy, Status, RequestedAtUtc, UpdatedAtUtc), UPDATE(Status, StatusText, UpdatedAtUtc) |
| eps_tick | Engine | JobSchedule | SELECT, INSERT, UPDATE |
| eps_tick | Engine | License | SELECT(Id, the license text) |
| eps_tick | Engine | Run | SELECT, INSERT, UPDATE |
| eps_tick | Engine | Setting | SELECT |
| eps_web | Web app | AdminUnit | SELECT |
| eps_web | Web app | AdUserSnapshotInfo | SELECT |
| eps_web | Web app | AuditEvent | SELECT, INSERT |
| eps_web | Web app | BreakGlassAccount | SELECT |
| eps_web | Web app | CaAdoption | SELECT |
| eps_web | Web app | CaAnalysis | SELECT |
| eps_web | Web app | CaAnalysisResult | SELECT |
| eps_web | Web app | CaBackup | SELECT |
| eps_web | Web app | CaBackupItem | SELECT |
| eps_web | Web app | CaDirectoryName | SELECT |
| eps_web | Web app | CaFinding | SELECT |
| eps_web | Web app | CaGraphOption | SELECT |
| eps_web | Web app | CaMonitor | SELECT |
| eps_web | Web app | CaPersona | SELECT |
| eps_web | Web app | CaPolicy | SELECT |
| eps_web | Web app | CaStageMove | SELECT |
| eps_web | Web app | CaTemplateTaken | SELECT |
| eps_web | Web app | CaVersionPair | SELECT |
| eps_web | Web app | ConditionType | SELECT |
| eps_web | Web app | ConfigBackup | SELECT |
| eps_web | Web app | ConfigBackupItem | SELECT |
| eps_web | Web app | ConfigChange | SELECT |
| eps_web | Web app | DeviceTagRule | SELECT |
| eps_web | Web app | DeviceTagRuleCondition | SELECT |
| eps_web | Web app | GroupRuleOverride | SELECT |
| eps_web | Web app | IdentityAction | SELECT |
| eps_web | Web app | IdentityLaRun | SELECT |
| eps_web | Web app | IdentityRow | SELECT |
| eps_web | Web app | IdentitySnapshot | SELECT |
| eps_web | Web app | IdentityViewMember | SELECT |
| eps_web | Web app | JobRequest | SELECT, INSERT |
| eps_web | Web app | JobSchedule | SELECT |
| eps_web | Web app | License | SELECT, UPDATE |
| eps_web | Web app | ManagedGroup | SELECT |
| eps_web | Web app | OnboardingCheck | SELECT |
| eps_web | Web app | PendingChange | SELECT, INSERT, UPDATE |
| eps_web | Web app | ProLicensePrice | SELECT, INSERT, UPDATE |
| eps_web | Web app | ProPasskeyProgress | SELECT |
| eps_web | Web app | ProPasskeyTarget | SELECT, INSERT, UPDATE |
| eps_web | Web app | ProPolicyCodeSync | SELECT |
| eps_web | Web app | ProStaleDecision | SELECT, INSERT, UPDATE |
| eps_web | Web app | Run | SELECT |
| eps_web | Web app | RunLog | SELECT |
| eps_web | Web app | SavedView | SELECT, INSERT, UPDATE, DELETE |
| eps_web | Web app | SchemaVersion | SELECT |
| eps_web | Web app | Scope | SELECT |
| eps_web | Web app | ServiceTagLocation | SELECT |
| eps_web | Web app | Setting | SELECT |
| eps_web | Web app | SupportEvent | SELECT |
| eps_web | Web app | TagChange | SELECT |
| eps_web | Web app | TagRuleHit | SELECT |
| eps_web | Web app | TagType | SELECT |
| eps_web | Web app | TagValue | SELECT |
| eps_web | Web app | TamperEvent | SELECT, UPDATE(Status, ResolvedBy, ResolvedAtUtc) |
| eps_web | Web app | TargetGroup | SELECT |
| eps_web | Web app | UplinkState | SELECT |
| eps_web | Web app | UserTagRule | SELECT |
| eps_web | Web app | UserTagRuleCondition | SELECT |
| eps_web | Web app | WebAccess | SELECT |
| eps_web | Web app | WorkerHeartbeat | SELECT |
| eps_web | Web app | WorkPackage | SELECT |
| eps_worker | Hybrid worker | AdGroupMember | SELECT, INSERT, DELETE |
| eps_worker | Hybrid worker | AdGroupSnapshotInfo | SELECT, INSERT, UPDATE |
| eps_worker | Hybrid worker | AdUserSnapshot | SELECT, INSERT, DELETE |
| eps_worker | Hybrid worker | AdUserSnapshotInfo | SELECT, UPDATE |
| eps_worker | Hybrid worker | Setting | SELECT |
| eps_worker | Hybrid worker | TagChange | SELECT, UPDATE(Status, Error, Via, AtUtc) |
| eps_worker | Hybrid worker | WorkerHeartbeat | SELECT, INSERT, UPDATE |
| eps_worker | Hybrid worker | WorkPackage | SELECT, UPDATE(Status, ClaimedBy, ClaimedAtUtc, Attempts, Error, DoneAtUtc, Result) |
| Script | Right | Scope | Why |
|---|---|---|---|
| Install-EntraPolicySuite.ps1 | Owner | the subscription | creates the resource group and resources and the role assignments |
| Install-EntraPolicySuite.ps1 | Global Administrator or Privileged Role Administrator | the tenant | grants the engine identity Graph and Exchange app roles and the Exchange Recipient Administrator role; creates the web sign-in registration and gives its admin consent |
| Deploy-EpsAzureMonitorAlerts.ps1 | Contributor (Monitoring Contributor is enough for the rules and the action group) | the Entra Policy Suite resource group | creates the action group and the scheduled query rules |
| Deploy-EpsAzureMonitorAlerts.ps1 | Security Administrator or Global Administrator | the tenant | creates the Entra diagnostic setting that sends AuditLogs and the sign-in logs to Log Analytics (only if none does yet) |
| Deploy-EpsAzureMonitorAlerts.ps1 | an Admin or SuperAdmin of the web app | the Entra Policy Suite web app | downloads the alarm inputs file (Tamper > Download the alarm inputs) - the script reads no database |
| Grant-EpsEnginePermissions.ps1 | Privileged Role Administrator (or Global Administrator) | the tenant | grants application permissions and the Exchange Recipient Administrator role to a managed identity |
| Grant-EpsEnginePermissions.ps1 | Reader | the Entra Policy Suite resource group | finds the engine identity |
| Grant-EpsHybridWorkerAccess.ps1 | Contributor (or any role that may start a Container Apps job) | the install's resource group | starts the database setup job, which creates (or removes) the database user and reads it back - no database right needed (installs made since 2.0.59) |
| Grant-EpsHybridWorkerAccess.ps1 | the SQL server's Entra admin - only for an install made before 2.0.59 | the Entra Policy Suite database | creates the database user directly |
| Grant-EpsUpdaterPermissions.ps1 | Privileged Role Administrator (or Global Administrator) | the tenant | grants application permissions to a managed identity |
| Grant-EpsUpdaterPermissions.ps1 | Reader | the Entra Policy Suite resource group | finds the update identity |
| Import-EpsV1Data.ps1 | read access to the v1 folder | the v1 DATA folder | the CSV files and configs are read |
| Initialize-EpsHybridWorkerAd.ps1 | an AD admin who may change the permissions of the OUs | the OUs named | adds the ACEs |
| Initialize-EpsLogAnalytics.ps1 | Contributor + User Access Administrator (or Owner) | the workspace's resource group (and the data collection resource group, if another) | creates the table, the endpoint and the rule, and the engine's two role assignments |
| Initialize-EpsLogAnalytics.ps1 | Contributor (or any role that may start a Container Apps job) | the install's resource group | starts the database setup job, which stores the target (audited) - no database right needed |
| Initialize-EpsMailSender.ps1 | Exchange Administrator (and Global Administrator for the Graph grant check) | the tenant | creates the mailbox, the Exchange service principal, the scope and the role assignment |
| Initialize-EpsMailSender.ps1 | Contributor (or any role that may start a Container Apps job) | the install's resource group | starts the database setup job, which stores the mail settings (audited) - no database right needed (installs made since 2.0.59); an older install: the SQL server's Entra admin stores them directly |
| Install-EpsHybridWorker.ps1 | local Administrator on the worker | the worker machine | copies the code, sets ACLs, user rights and a scheduled task |
| Set-EpsWebAccess.ps1 | Contributor (or any role that may start a Container Apps job) | the install's resource group | starts the database setup job, which names the SuperAdmin when there is none - no database right needed (installs made since 2.0.59) |
| Set-EpsWebAccess.ps1 | the Entra Policy Suite SQL server's Entra admin - only for an install made before 2.0.59 (no database setup job) | the Entra Policy Suite database | writes the access list directly |
| Set-EpsWebAccess.ps1 | any user who can read users (Directory Readers is enough) | the tenant | checks the person exists and pins their object id |
| Start-EpsInstall.ps1 | the rights Install-EntraPolicySuite.ps1 needs | see its page | this script only downloads and starts it |
4. Data
Data read from your tenant
- Users and devices: their properties, the tag attributes, group memberships, license plans and last sign-in time (for the rules and the identity report).
- Conditional Access policies, named locations, directory role assignments and application names (Conditional Access page and CA Policy Analyzer).
- Intune managed devices (identity report) and the directory audit log entries for watched tags (tamper monitor).
- Exchange recipient types (Exchange rule conditions).
- On-premises AD users and groups (hybrid worker only).
Data written to your tenant
- The tag attributes of your tag types on users and devices (Entra ID through Microsoft Graph; AD-synced users in Active Directory through the hybrid worker).
- A mail-enabled user's custom attribute through Exchange Online, where Graph does not write it.
- One adopted Conditional Access policy moved one stage, only when a SuperAdmin asks for it.
Data stored
All in your Azure SQL database (table list below), your Log Analytics workspace and your Key Vault. Values whose name marks them as a credential are removed before an audit row is written. Encryption at rest and in transit is Azure's: Azure SQL with TLS 1.2 or later, the storage account with TLS 1.2 or later and HTTPS only.
Data sent to Invardia (the uplink)
The engine reports to Invardia only when Invardia has issued a key for this install and that key is in the install's Key Vault. Without it, nothing is sent. With it, the uplink job sends a heartbeat every 4 hours and a record of each finished run, over HTTPS to invardia.com, at most 100 records per cycle. Each record carries only the fields listed below; any other field is dropped before it leaves the install. Free text (an error line) has mail addresses, user names, object ids, security ids and quoted values removed first, so the uplink carries counts and states, not who. A SuperAdmin can switch the uplink off (Uplink.Enabled); the security findings record is off by default (Uplink.Alerts.Enabled).
The update check
Every night the update job asks invardia.com which Entra Policy Suite version is approved for this install's update ring. That question carries no key and nothing about your tenant: only the ring. The job checks that the answer is genuine and complete before it uses it, and records the result in its own run. With the per-install key it also asks whether Invardia has asked for an update now, and reports once per new version that it is available (the running version, the new version and the ring). In this release it installs nothing.
The license (Pro and the trial)
Entra Policy Suite runs as Community without a license; nothing is sent for a license then. An install made with the Pro install line, or one that holds a Pro license, asks Invardia for its license from the update job: the product name, the running version, the tenant id, the install's own id and - when a license is already held - its id, end date and SHA-256. Invardia answers with a request number; the job collects the license later. The license file is signed by Invardia and names the tenants it is for: the job checks it against the public certificates built into the release, the product, the tenant and the dates before it keeps anything, and every part of the product checks it again whenever it reads it. With a valid Pro license the job asks Invardia once for the per-install key (sending the license and the tenant id). The license and that key are kept in the install's own Key Vault; the license also in the database. Nobody pastes or uploads a license. The heartbeat then carries the edition, the license's state and its id. Pro and the 60-day trial are ordered at invardia.com.
| Table | Written by | Read by |
|---|---|---|
| AdGroupMember | Hybrid worker | Engine, Hybrid worker |
| AdGroupSnapshotInfo | Engine, Hybrid worker | Engine, Hybrid worker |
| AdminUnit | Engine | Engine, Web app |
| AdUserSnapshot | Hybrid worker | Engine, Hybrid worker |
| AdUserSnapshotInfo | Engine, Hybrid worker | Engine, Web app, Hybrid worker |
| AuditEvent | Engine, Support read job, Web app | Engine, Support read job, Web app |
| BreakGlassAccount | Engine | Engine, Web app |
| CaAdoption | Engine | Engine, Web app |
| CaAnalysis | Engine | Engine, Web app |
| CaAnalysisResult | Engine | Engine, Web app |
| CaBackup | Engine | Engine, Web app |
| CaBackupItem | Engine | Engine, Web app |
| CaDirectoryName | Engine | Engine, Web app |
| CaFinding | Engine | Engine, Web app |
| CaGraphOption | Engine | Engine, Web app |
| CaMonitor | Engine, Engine | Engine, Engine, Web app |
| CaPersona | Engine | Engine, Web app |
| CaPolicy | Engine | Engine, Web app |
| CaStageMove | Engine | Engine, Web app |
| CaTemplateTaken | Engine | Engine, Web app |
| CaVersionPair | Engine | Engine, Web app |
| ConditionType | Engine | Engine, Web app |
| ConfigBackup | Engine | Engine, Web app |
| ConfigBackupItem | Engine | Engine, Web app |
| ConfigChange | Engine | Engine, Web app |
| DeviceTagRule | Engine | Engine, Web app |
| DeviceTagRuleCondition | Engine | Engine, Web app |
| GroupPin | Engine | Engine |
| GroupRuleOverride | Engine | Engine, Web app |
| IdentityAction | Engine | Engine, Web app |
| IdentityLaRun | Engine | Engine, Web app |
| IdentityRow | Engine | Engine, Web app |
| IdentitySnapshot | Engine | Engine, Web app |
| IdentityViewMember | Engine | Engine, Web app |
| JobRequest | Engine, Engine, Web app | Engine, Engine, Web app |
| JobSchedule | Engine, Engine | Engine, Support read job, Engine, Web app |
| License | Engine, Web app | Engine, Support read job, Engine, Web app |
| LicensePull | Engine | Engine |
| ManagedGroup | Engine | Engine, Web app |
| MonitorState | Engine | Engine |
| OnboardingCheck | Engine | Engine, Web app |
| PendingChange | Engine, Web app | Engine, Web app |
| ProLicensePrice | Web app | Engine, Web app |
| ProPasskeyProgress | Engine | Engine, Web app |
| ProPasskeyTarget | Engine, Web app | Engine, Web app |
| ProPolicyCodeSync | Engine | Engine, Web app |
| ProStaleDecision | Engine, Web app | Engine, Web app |
| RefreshChangedStats | - | - |
| Run | Engine, Support read job, Engine | Engine, Support read job, Engine, Web app |
| RunLog | Engine | Engine, Web app |
| SavedView | Web app | Web app |
| SchemaVersion | - | Web app |
| Scope | Engine | Engine, Web app |
| ServiceTagLocation | Engine | Engine, Web app |
| ServiceTagPrefix | Engine | Engine |
| Setting | Engine | Engine, Support read job, Engine, Web app, Hybrid worker |
| SupportAccess | - | Engine |
| SupportEvent | Engine | Engine, Web app |
| TagChange | Engine, Hybrid worker | Engine, Web app, Hybrid worker |
| TagRuleHit | Engine | Engine, Web app |
| TagType | Engine | Engine, Web app |
| TagValue | Engine | Engine, Web app |
| TamperEvent | Engine, Web app | Engine, Web app |
| TargetGroup | Engine | Engine, Web app |
| UplinkState | Engine | Engine, Web app |
| UserTagRule | Engine | Engine, Web app |
| UserTagRuleCondition | Engine | Engine, Web app |
| WatchedValue | Engine | Engine |
| WebAccess | Engine | Engine, Web app |
| WorkerHeartbeat | Hybrid worker | Engine, Support read job, Web app, Hybrid worker |
| WorkPackage | Engine, Hybrid worker | Engine, Web app, Hybrid worker |
| Data | Where | Kept |
|---|---|---|
| Rules, tag types, values, condition types, settings, scopes and their version history | Your database | Until you change or delete them; the history stays |
| Runs, run logs and tag changes | Your database | In your database until you delete them |
| Identity report snapshots | Your database | The newest 7 by default (setting IdentityReport.KeepRuns) |
| Tamper events | Your database | In your database, with the decision made on each |
| Audit trail | Your database | Append-only: the database refuses to change or delete an audit row |
| Container logs | Your Log Analytics workspace | 30 days |
| Sign-in sessions of the web app | Your database (the sign-in front's own schema) | At most 8 hours per session |
| Record | Fields |
|---|---|
| heartbeat | Schema, Kind, Product, Version, Edition, Hosting, RuntimeIdentity, ClaimedTenantId, LastSeenUtc, Ring, Outcome, Counts, EnvironmentName, Jobs, Update, Mail, SignIn, Database, SetupSteps, Endpoints, Pipeline, SupportAccess, BuildPool, UplinkBacklog, LicenceState, LicenceId |
| run | Schema, Kind, Product, Version, Edition, Hosting, RuntimeIdentity, ClaimedTenantId, LastSeenUtc, Job, Outcome, DurationMs, ErrorClass, FixKind, ErrorText, Counts |
| alert | Schema, Kind, Product, Version, Edition, ClaimedTenantId, LastSeenUtc, AlertId, Severity, Measured, FirstSeenUtc, Count, Area, Job, ActionText, Link |
5. Access by the vendor
Entra Policy Suite is installed and run by you. The install creates no account, application, role, group or database user for Invardia, and Invardia has no access to your tenant, subscription or database.
What reaches Invardia
- Only the uplink of chapter 4, and only when Invardia has issued a key for this install. The key is kept in the install's own Key Vault; only the engine identity may read that one item, at send time. It is never written to a log, a job's settings or the database.
- The uplink only sends. Invardia's answer to a heartbeat may carry a support state (a support case open or closed). Entra Policy Suite records that state, shows it, and writes it to the audit trail. It grants nothing from it.
Support
- Community: questions and problems go to the Entra Policy Suite GitHub repository. You never share access to your environment; share run logs only after you remove identifying data.
- Pro: support from Invardia (the support service, the Support app and just-in-time database access - each optional). The Support app is in both editions and off until you create it; Community uses it optionally. Pro adds API and MCP access, always as the signed-in person, with their own role. Pro and the 60-day trial are ordered at invardia.com.
The license gives Invardia no access
A Pro license switches Pro features on in your install; it grants Invardia nothing. Every security control of Community - the roles, the commit queue, the guards, the audit trail, least privilege - is the same in Community and Pro: no security control exists only in Pro. The update job writes exactly two Key Vault items Invardia issues (the license and the per-install key), through Azure as its own identity, which may write them and cannot read any item back.
6. Supply chain
How code reaches your environment
- Entra Policy Suite is published as releases on GitHub. The install is two lines in PowerShell 7: line 1 downloads the launcher
Start-EpsInstall.ps1, line 2 runs it. The launcher downloads the release, checks it against the SHA-256 digest GitHub publishes for it, and deletes it and stops when it does not match. - The container images are built inside your own container registry from the release, by the registry's own build. No pre-built vendor image is pulled. The base images come from the Microsoft Container Registry, pinned by digest.
- Every script a person runs is REST and .NET only: no PowerShell module and no Azure CLI, in the install, the jobs or the hybrid worker. People sign in in a browser; no script asks for or stores a password.
- An update is a new release, installed the same way: the install is idempotent and resumes or converges an existing install.
- Pro: the Pro install line downloads the same launcher from invardia.com. It takes the Pro package from Invardia - never from GitHub - after the person signs in to the tenant it installs into; the package's description is signed by Invardia and checked against the keys built into the launcher (product, edition, update ring, version, size and SHA-256) before anything runs. A Pro install's updates come the same way (signed, the same checks). The Pro package is the Community release plus the Pro features' own folder.
The image
The facts below are read from the image's build file of this release.
| Item | Value | Note |
|---|---|---|
| Base image (sqldriver) | mcr.microsoft.com/dotnet/sdk:8.0 | Pinned by digest sha256:5ef85cc12cb25be6ec319a7392d1e9efd53c3bc8abb971c53d8058a473f09053 |
| Base image (src) | mcr.microsoft.com/powershell:7.4-ubuntu-22.04 | Pinned by digest sha256:62300a213a9293916333df2b014cd3a8f22fb0b0b65f2bb446aaf436bcf8c868 |
| Base image (runtime) | mcr.microsoft.com/powershell:7.4-ubuntu-22.04 | Pinned by digest sha256:62300a213a9293916333df2b014cd3a8f22fb0b0b65f2bb446aaf436bcf8c868 |
| Runs as | User 10001:10001 | A non-root user; the image writes only to its temp folder |
| SQL driver | Microsoft.Data.SqlClient 5.2.2 | A pinned .NET assembly, not a PowerShell module |
| Left out of every layer | tests, internal, output, docs, CLAUDE.md | Removed in a build stage before the runtime image is made |
| PowerShell modules | None | The gallery tooling is removed; the build fails if a banned module or the Azure CLI is still in the image |
| Sign-in front | Version 1.4.5 | Pinned by this release; built in your registry from its source |
7. Change control and audit
Every change is checked, staged and recorded
- Rule, tag type, tag value and condition type changes are checked, then staged in the commit queue (on by default: true). An admin commits them; a change to something edited in the meantime fails. A second approver can be required.
- A change asked for in the web app is carried out by the engine, which first verifies the person's sign-in proof and checks their role at that moment. The web app's own identity cannot change the directory or the configuration.
- Every setting, schedule and access change needs a reason. The protection settings below need a SuperAdmin.
- Every rule, tag type, setting and scope keeps its version history; an earlier version can be put back as a new change.
Runs plan before they write
- A tagging job's mode is Preview after the install: it records its plan and writes nothing until an admin chooses Apply. Every feature is off after the install.
- A run can be limited to a saved test scope.
- The mass-change guard holds a run whose plan changes far more objects than usual, with a lower limit for overwriting an existing tag than for filling an empty one. Nothing is applied until an admin approves; the hold is recorded and sent as an alert.
- Break-glass accounts you list are never targeted.
Audit trail
One writer for every part (web app, jobs, setup) and a closed list of actions (below): an action not on the list is not written, and the release's tests fail. The audit table is append-only: no role may update or delete a row, and the database refuses it. Each row has who, when, from where (web, job or setup), the target, the reason, the outcome, and before and after. Refused requests and guard holds are recorded too. Exports of audited data are themselves audited.
| Setting | Where | Least role | What it does |
|---|---|---|---|
| Jobs run (master switch) | Settings > General | SuperAdmin | All jobs stop at once |
| Commit queue for rule changes | Settings > Commit queue | SuperAdmin | Rule changes wait for a commit |
| Second approver | Settings > Commit queue | SuperAdmin | A second person must approve a commit |
| Guard: most new tag changes per run | Settings > User tagging | Admin | A limit of the mass-change guard: a run whose plan exceeds it is held for approval |
| Guard: most new tag changes (percent of users) | Settings > User tagging | Admin | A limit of the mass-change guard: a run whose plan exceeds it is held for approval |
| Guard: most new overwrites per run | Settings > User tagging | Admin | A limit of the mass-change guard: a run whose plan exceeds it is held for approval |
| Guard: most new overwrites (percent of users) | Settings > User tagging | Admin | A limit of the mass-change guard: a run whose plan exceeds it is held for approval |
| Guard: most new tag changes per run | Settings > Device tagging | Admin | A limit of the mass-change guard: a run whose plan exceeds it is held for approval |
| Guard: most new tag changes (percent of devices) | Settings > Device tagging | Admin | A limit of the mass-change guard: a run whose plan exceeds it is held for approval |
| Guard: most new overwrites per run | Settings > Device tagging | Admin | A limit of the mass-change guard: a run whose plan exceeds it is held for approval |
| Guard: most new overwrites (percent of devices) | Settings > Device tagging | Admin | A limit of the mass-change guard: a run whose plan exceeds it is held for approval |
| Tamper monitor | Settings > Tamper monitor | SuperAdmin | Watched tags are compared with what Entra Policy Suite wrote |
| Known writers (allow list) | Settings > Tamper monitor | SuperAdmin | Writers whose changes are not tamper events |
| High-severity tag values | Settings > Tamper monitor | Admin | Tag values whose change is a high-severity event |
| Alert mail | Settings > Alert mail | SuperAdmin | Whether alert mail is sent |
| Sender mailbox | Settings > Alert mail | SuperAdmin | The one mailbox the engine may send from |
| Alert recipients | Settings > Alert mail | SuperAdmin | Who receives alert mail |
| SMTP server | Settings > Alert mail | SuperAdmin | The SMTP relay alert mail goes through, when alert mail is sent by SMTP |
| SMTP port | Settings > Alert mail | SuperAdmin | The port of that relay |
| SMTP with TLS | Settings > Alert mail | SuperAdmin | Whether the connection to that relay is encrypted |
| SMTP from address | Settings > Alert mail | SuperAdmin | The address mail through that relay comes from |
| SMTP password (Key Vault secret) | Settings > Alert mail | SuperAdmin | Where the relay password is kept (a Key Vault secret; the password itself is never stored) |
| Conditional Access monitor (adopted policies) | Settings > Conditional Access | SuperAdmin | Adopted Conditional Access policies are compared with the recorded state |
| Send the report to Log Analytics (and read the exposure lists) | Settings > Identity report | SuperAdmin | Whether the identity report's user rows go to Log Analytics |
| Log Analytics target (workspace, endpoint, rule) | Settings > Identity report | SuperAdmin | The one workspace, endpoint and rule the user rows go to |
| Send telemetry to Invardia | Settings > Invardia telemetry | SuperAdmin | Whether the Invardia uplink sends |
| Send security findings to Invardia | Settings > Invardia telemetry | SuperAdmin | Whether security findings are sent to Invardia |
| Install new versions | Settings > Updates | SuperAdmin | Whether the update job checks for new versions |
| Allow the clean-up to disable approved accounts | Settings > Pro: stale identities | SuperAdmin | Whether the stale identity clean-up may disable (and enable again) accounts a SuperAdmin approved - the engine holds User.ReadWrite.All for it only while this is on |
| Back up every night | Settings > Backups | SuperAdmin | Whether the policies and the tagging configuration are backed up every night |
| Git provider | Settings > Pro: policy as code | SuperAdmin | Pro policy as code: the Git provider (GitHub or Azure DevOps) the policies are pushed to and read from |
| Git repository | Settings > Pro: policy as code | SuperAdmin | Pro policy as code: the Git repository the policies are pushed to and read from |
| Git branch | Settings > Pro: policy as code | SuperAdmin | Pro policy as code: the branch a push commits to and a read takes by default |
| Folder in the repository | Settings > Pro: policy as code | SuperAdmin | Pro policy as code: the folder in the repository that holds the policies |
| Git access key (Key Vault secret) | Settings > Pro: policy as code | SuperAdmin | Pro policy as code: where the Git access key is kept (a Key Vault secret; the key itself is never stored) |
| Action | Area |
|---|---|
| auth.request.refused | Sign-in |
| support.read | Support access |
| access.grant | Access list |
| access.revoke | Access list |
| job.run.request | Jobs |
| job.test.request | Jobs |
| job.golive.request | Jobs |
| config.request | Configuration |
| job.schedule.change | Jobs |
| queue.stage | Commit queue |
| queue.commit | Commit queue |
| queue.discard | Commit queue |
| queue.discard-all | Commit queue |
| mail.test.request | |
| setting.change | Settings |
| license.claim.again | License |
| rule.create | Rules |
| rule.update | Rules |
| rule.delete | Rules |
| rule.move | Rules |
| rule.batch | Rules |
| rule.batch.check | Rules |
| rule.import.plan | Rules |
| rules.export | Rules |
| tagtype.change | Tag types |
| tagtype.delete | Tag types |
| tagvalue.change | Tag types |
| tagvalue.remove | Tag types |
| condition.change | Condition types |
| condition.delete | Condition types |
| scope.change | Scopes |
| scope.delete | Scopes |
| ca.analyze.request | Conditional Access |
| ca.export | Conditional Access |
| ca.monitor.change | Conditional Access |
| ca.monitor.delete | Conditional Access |
| ca.template.take | Conditional Access |
| ca.persona.change | Conditional Access |
| ca.persona.delete | Conditional Access |
| ca.policy.adopt | Conditional Access |
| ca.policy.release | Conditional Access |
| identity.action.set | Identity report |
| onboarding.check | Get started |
| breakglass.change | Break-glass accounts |
| onboarding.strengths | Get started |
| onboarding.deniedcountries | Get started |
| onboarding.trusted | Get started |
| onboarding.breakglass | Get started |
| ca.policy.stage | Conditional Access |
| ca.policy.edit | Conditional Access |
| ca.policy.clone | Conditional Access |
| ca.policy.edit.check | Conditional Access |
| ca.directory.search | Conditional Access |
| ca.policy.deploy | Conditional Access |
| ca.policy.newversion | Conditional Access |
| ca.policy.promote | Conditional Access |
| ca.policy.rollback | Conditional Access |
| ca.policy.removeold | Conditional Access |
| ca.group.check | Conditional Access |
| ca.group.create | Conditional Access |
| ca.group.rule | Conditional Access |
| ca.group.members | Conditional Access |
| ca.group.forceupdate | Conditional Access |
| ca.group.au | Conditional Access |
| ca.group.editrule | Conditional Access |
| ca.group.validate | Conditional Access |
| ca.policy.restore | Conditional Access |
| ca.policy.restore.check | Conditional Access |
| ca.policy.undo | Conditional Access |
| job.run.undo | Jobs |
| backup.ca.export | Backups and undo |
| ca.policy.remove | Conditional Access |
| ca.policy.remove.check | Conditional Access |
| ca.object.restore | Conditional Access |
| ca.object.restore.check | Conditional Access |
| config.export | Configuration |
| config.import.check | Configuration |
| ca.documentation.export | Conditional Access |
| identity.run.request | Identity report |
| identity.export | Identity report |
| view.save | Identity report |
| view.delete | Identity report |
| tamper.event.accept | Tamper monitor |
| tamper.event.restore.request | Tamper monitor |
| tamper.event.restore | Tamper monitor |
| audit.export | Exports |
| changes.export | Exports |
| runs.export | Exports |
| web.request.refused | Web requests |
| guard.trip | Guards |
| support.state.change | Support access |
| support.session.refused | Support access |
| v1import.plan | Import from v1 |
| v1import.apply | Import from v1 |
| support.access.change | Support access |
| auth.signin | Sign-in |
| identity.view | Identity report |
| ca.analysis.view | Conditional Access |
| tamper.alarm-inputs.export | Tamper monitor |
| setup.database.apply | Database setup |
| update.apply | Updates |
| pro.licenseopt.export | Pro features |
| pro.licenseprice.change | Pro features |
| pro.policycode.export | Pro features |
| pro.policycode.push | Pro features |
| pro.policycode.import | Pro features |
| worker.test.request | Hybrid worker |
| worker.retry | Hybrid worker |
| pro.passkey.target | Pro features |
| pro.stale.decide | Pro features |
| pro.stale.approve | Pro features |
| pro.stale.reenable | Pro features |
| api.use | API for scripts (Pro) |
| mcp.tool | MCP server (Pro) |
8. Threat model and hardening
The top risks for a product that writes the attributes your Conditional Access policies depend on, and the controls that answer each one. Every control below is part of the design: it is built into the product, applied by the install and kept in place by every re-run of it. The checklist after it lists what you can add in your own tenant.
| Threat | Controls | Hardening by design |
|---|---|---|
| Someone changes tags to move users between Conditional Access policies | Changes go through the commit queue (optional second approver); runs plan first (Preview); the mass-change guard holds unusual runs; every change is audited with who and why | Commit queue on by default; tagging in Preview after the install |
| A tag is changed outside Entra Policy Suite | The tamper monitor compares watched tags with what Entra Policy Suite wrote, names the writer from the audit log, raises an event and can restore the value | High-severity values (for example break-glass) are flagged |
| Misuse of the engine identity | Managed identity (no credential to steal); it also runs the schedule check, so no separate scheduler identity exists; Graph rights read only except the tag writes and one Conditional Access stage move a SuperAdmin asks for; any Graph right can be left out at install | Rights granted at install, listed in chapter 3 and read back |
| A web app session is misused | The web app holds no directory right; the engine verifies the person's sign-in proof and checks their role at the moment it acts; sessions end after a fixed time | Assignment required on the sign-in registration; your Conditional Access applies to the sign-in |
| Data is read or changed in the database | Entra authentication only; one role per part with object-level grants; the audit table is append-only | No part is db_owner, db_datareader or db_datawriter |
| A tampered release or image | The launcher checks the release against the SHA-256 digest GitHub publishes; images are built in your registry from base images pinned by digest; no modules; non-root user | Build fails if a banned module is present |
| Data leaves the tenant | Nothing is sent to Invardia without a per-install key; the uplink carries listed fields only and removes names and ids from free text | The uplink can be switched off by a SuperAdmin |
| Exposure from the internet | Private install option: data services with public access off, private endpoints, internal environment, proven from inside the network | Shared keys are off on the storage account in every install |
| An attempt to lock everyone out | The last SuperAdmin cannot be removed (a guard); every access change is audited | Name more than one SuperAdmin |
| # | Action | Why |
|---|---|---|
| 1 | Name at least two SuperAdmins | Access never depends on one person |
| 2 | Assign only the people and groups who need the web app to its enterprise application | Only assigned people can sign in |
| 3 | Apply your Conditional Access policies (MFA, compliant device) to the web app's sign-in | The sign-in is an ordinary Entra sign-in |
| 4 | List your break-glass accounts in the product | They are never targeted |
| 5 | Turn on the second approver for the commit queue | Four eyes on every rule change |
| 6 | Turn on the tamper monitor and alert mail | Changes made outside Entra Policy Suite are reported |
| 7 | Keep the SQL admin group (grp-eps-sql-admins, or your own with -SqlAdminGroupId) to the database setup job's identity | No person holds standing database admin rights |
| 8 | Deploy the Azure Monitor alarm (Deploy-EpsAzureMonitorAlerts.ps1) | Alerts that work independently of the product |
| 9 | Use the private install where your policy asks for it | No public endpoint for data services or the web app |
| 10 | Leave out any Graph permission your approver does not approve (-ExcludeGraphRole) | Only approved rights are granted |
9. Compliance mapping
How Entra Policy Suite's controls can support your own controls. This is not a certification and does not make your organization compliant; you remain responsible for your controls and their evidence.
| Framework | Control | How Entra Policy Suite supports it |
|---|---|---|
| ISO/IEC 27001:2022 Annex A | 5.15 Access control / 5.18 Access rights | Users and devices are tagged from rules you control, so Conditional Access and dynamic groups follow role and status consistently |
| ISO/IEC 27001:2022 Annex A | 5.16 Identity management | The identity report finds inactive, orphaned and unclassified accounts every day |
| ISO/IEC 27001:2022 Annex A | 8.2 Privileged access rights | Privileged accounts are identified and their protection is reported; break-glass accounts are listed and never targeted |
| ISO/IEC 27001:2022 Annex A | 8.15 Logging | Append-only audit trail of every change and refused request, with who, when, why, before and after |
| ISO/IEC 27001:2022 Annex A | 8.32 Change management | Commit queue with optional second approver; Preview before Apply; mass-change guard |
| NIS2 (EU 2022/2555) Article 21 | (i) access control policies, (j) multi-factor authentication | Conditional Access targeting kept in line with your rules; accounts without strong authentication reported |
| DORA (EU 2022/2554) Article 9 | Protection and prevention: identity and access management | Tagging from rules, reviewed changes, and an audit trail you keep in your own database |
| CIS Controls v8 | 5 Account management / 6 Access control management | Inactive and unclassified accounts found; access follows tags set from rules |
10. Uninstall and exit
Removing the product
- The Azure side: delete the resource group the product was installed in. That removes the jobs, the web app, the Container Apps environment, the database (all stored data), the queues, the registry, the Log Analytics workspace, the Key Vault and the managed identities. With the managed identities go their Microsoft Graph and Exchange permissions and their directory role.
- The Key Vault stays recoverable for 90 days after it is deleted (purge protection); it holds no data of yours.
- Outside the resource group: delete the web sign-in registration in Entra ID, and undo the optional parts with the scripts that made them (table below).
- What stays: the tags already written on your users and devices; remove them with your own tooling if you want them gone. Your Conditional Access policies are yours and are not removed.
- Exit is complete: nothing runs outside your subscription, so nothing remains at Invardia beyond the uplink records it already received.
| Script | Undo |
|---|---|
| Install-EntraPolicySuite.ps1 | Delete the resource group, and the engine identity's app role and Exchange role assignments (they go with the identity when the resource group is deleted). |
| Deploy-EpsAzureMonitorAlerts.ps1 | Delete ag-eps-alarm and the eps-* rules; delete the diagnostic setting eps-entra-logs if it was created by this script. |
| Grant-EpsEnginePermissions.ps1 | Run again with -Remove (or without it, to grant back). |
| Grant-EpsHybridWorkerAccess.ps1 | Run again with -Remove. |
| Grant-EpsUpdaterPermissions.ps1 | Run again with -Remove (or without it, to grant back). |
| Import-EpsV1Data.ps1 | Nothing to undo: the script writes nothing. An import made on the Import v1 page is journaled in the change history and its rules can be removed as a set; the v1 install is never changed, so v1 keeps running until it is switched off. |
| Initialize-EpsHybridWorkerAd.ps1 | Run again with -Remove -Apply. |
| Initialize-EpsLogAnalytics.ps1 | Switch 'Send the report to Log Analytics' off; remove the two role assignments, the rule, the endpoint and (if wanted) the table. |
| Initialize-EpsMailSender.ps1 | Remove the role assignment, the scope, the Exchange service principal and (if created) the mailbox; set Mail.Mode to none. |
| Install-EpsHybridWorker.ps1 | -Remove: both tasks, the group membership and the code; then Initialize-EpsHybridWorkerAd.ps1 -Remove and Grant-EpsHybridWorkerAccess.ps1 -Remove. |
| Set-EpsWebAccess.ps1 | A SuperAdmin removes the person on the app's Access page. |
| Start-EpsInstall.ps1 | Nothing to undo here; see Install-EntraPolicySuite.ps1. |
11. Scripts
Scripts are run by highly privileged people, so they are listed one by one with the rights needed, the changes made and how to preview them. Every script is:
- self-contained, REST and .NET only - no PowerShell module and no Azure CLI;
- signed in to in a browser as the person running it - no password, no client secret and no certificate in or out;
- able to show its plan first (-WhatIf, or no -Apply) without changing anything;
- shown as two lines, download then run - never piped into Invoke-Expression;
- listed in SHA256SUMS.txt next to it, for whoever verifies a download.
The install
- Download the launcher:
Invoke-WebRequest https://github.com/KnudsenMorten/EntraPolicySuite/releases/latest/download/Start-EpsInstall.ps1 -OutFile Start-EpsInstall.ps1 - Run it (add -WhatIf first to see the plan; nothing is created):
./Start-EpsInstall.ps1 -TenantId <tenant id> -SubscriptionId <subscription id>
The launcher checks the release and runs its Install-EntraPolicySuite.ps1 with the same parameters; that script is part of the release package and is listed separately below.
Every other script
Invoke-WebRequest https://invardia.com/support/eps/<Script>.ps1 -OutFile <Script>.ps1./<Script>.ps1 -WhatIf, then without -WhatIf.
| Script | Purpose | Rights needed to run it | Changes it makes | Preview |
|---|---|---|---|---|
| Start-EpsInstall.ps1 | Download Entra Policy Suite 2.0 and start the install | the rights Install-EntraPolicySuite.ps1 needs (see its page) | this computer: create a temporary folder with the release zip and its contents | Downloads and checks the release, then runs the installer with -WhatIf (its plan; nothing is changed in Azure). |
| Deploy-EpsAzureMonitorAlerts.ps1 | Deploy Entra Policy Suite's external alarm in Azure Monitor | Contributor (Monitoring Contributor is enough for the rules and the action group) (the Entra Policy Suite resource group); Security Administrator or Global Administrator (the tenant); an Admin or SuperAdmin of the web app (the Entra Policy Suite web app) | Entra: create diagnostic setting eps-entra-logs (AuditLogs, SignInLogs, NonInteractiveUserSignInLogs -> the install's Log Analytics workspace), unless one already sends AuditLogs; Azure: create action group ag-eps-alarm (mail + SMS); Azure: create scheduled query rules eps-* (up to 10); Azure: remove eps-* rules no longer wanted (e.g. the AD rules when no AD break-glass account remains) | Every resource it would create, update or remove; nothing is changed. |
| Grant-EpsEnginePermissions.ps1 | Give the Entra Policy Suite engine a missing permission, or take one away | Privileged Role Administrator (or Global Administrator) (the tenant); Reader (the Entra Policy Suite resource group) | Entra: create app role assignments on the engine identity; Entra: create directory role assignment Exchange Recipient Administrator (with -Exchange) | What the engine has, and what would be granted or removed; nothing is changed. |
| Grant-EpsHybridWorkerAccess.ps1 | Give the hybrid worker's managed identity access to the Entra Policy Suite database | Contributor (or any role that may start a Container Apps job) (the install's resource group); the SQL server's Entra admin - only for an install made before 2.0.59 (the Entra Policy Suite database) | SQL: create database user eps-hybrid-worker | The T-SQL it would run; nothing is run. |
| Grant-EpsUpdaterPermissions.ps1 | Give the Entra Policy Suite update identity a missing permission, or take one away | Privileged Role Administrator (or Global Administrator) (the tenant); Reader (the Entra Policy Suite resource group) | Entra: create app role assignments on the update identity | What the update identity has, and what would be granted or removed; nothing is changed. |
| Import-EpsV1Data.ps1 | See what a v1 install's rules and settings become in Entra Policy Suite 2.0 | read access to the v1 folder (the v1 DATA folder) | The script always only reads and shows; -WhatIf changes nothing about that. | |
| Initialize-EpsHybridWorkerAd.ps1 | Delegate the Entra Policy Suite hybrid worker's gMSA in Active Directory | an AD admin who may change the permissions of the OUs (the OUs named) | Active Directory: grant the gMSA | Without -Apply (or with -WhatIf): every ACE it would add or remove and the ones already there. |
| Initialize-EpsLogAnalytics.ps1 | Send Entra Policy Suite's identity report to Log Analytics and read the exposure lists | Contributor + User Access Administrator (or Owner) (the workspace's resource group (and the data collection resource group, if another)); Contributor (or any role that may start a Container Apps job) (the install's resource group) | Azure: create the table EntraUsersMetadata_CL (or its missing columns; existing columns and their types are kept); Azure: create the data collection endpoint (default dce-entrapolicysuite) and the rule dcr-entra-EntraUsersMetadata_CL; Azure: grant the engine identity; Azure: grant the engine identity; SQL: update setting IdentityReport.LogAnalytics.Target | Every step it would take; nothing is changed. |
| Initialize-EpsMailSender.ps1 | Set up Entra Policy Suite's mail: a shared mailbox, sent from by the engine only | Exchange Administrator (and Global Administrator for the Graph grant check) (the tenant); Contributor (or any role that may start a Container Apps job) (the install's resource group) | Exchange Online: create the shared mailbox (if it does not exist; no license); Exchange Online: create service principal for the engine identity, management scope <sender>-SendScope; Exchange Online: grant the engine identity; Entra: remove a tenant-wide Graph Mail.Send app role on the engine identity, if present; SQL: update settings Mail.Mode, Mail.Sender, Mail.Recipients (Platform.EnvironmentName) | Every step it would take and the settings it would store; nothing is changed. |
| Install-EpsHybridWorker.ps1 | Install the Entra Policy Suite hybrid worker on a domain-joined server | local Administrator on the worker (the worker machine) | the worker: create a folder on the machine (the worker code; the current and the previous version), the junction a folder on the machine pointing at the current one, a folder on the machine (the database, the worker name, the gMSA - no secret), a folder on the machine; the worker: update the ACL of a folder on the machine; the worker: grant the gMSA; the worker: grant the gMSA; the worker: create scheduled task 'EntraPolicySuite Hybrid Worker' (every 5 minutes, as the gMSA); the worker: create scheduled task 'EntraPolicySuite Hybrid Worker Update' (daily 05:30, as SYSTEM) | Every step it would take; nothing is copied, granted or registered. |
| Set-EpsWebAccess.ps1 | Name who may use the Entra Policy Suite web app (the first SuperAdmin) | Contributor (or any role that may start a Container Apps job) (the install's resource group); the Entra Policy Suite SQL server's Entra admin - only for an install made before 2.0.59 (no database setup job) (the Entra Policy Suite database); any user who can read users (Directory Readers is enough) (the tenant) | SQL: update eps.WebAccess (one row); SQL: create eps.AuditEvent (one row, access.grant) | The person, the role and who grants it; nothing is changed. |
| Script | Purpose | Rights needed to run it | Changes it makes | Preview |
|---|---|---|---|---|
| Install-EntraPolicySuite.ps1 | Install Entra Policy Suite 2.0 in your own Azure subscription | Owner (the subscription); Global Administrator or Privileged Role Administrator (the tenant) | 26 changes in Azure, Entra, SQL, this computer - every one is in chapters 1 to 3 | Every step, what exists and what it would create - nothing is created, registered or granted. |
Published scripts and their checksums
Every Entra Policy Suite script Invardia publishes at invardia.com/support/eps/, with its documentation page (what it does, the rights it needs, every change it makes, -WhatIf and undo) and the SHA-256 of the exact published file, computed when this page was built.
Always check the SHA-256 before you run a script.
| Script | Version | SHA-256 | Documentation |
|---|---|---|---|
| Deploy-EpsAzureMonitorAlerts | 2.0.107 | 66a3e55b5c377bf32de1d4f7419eebba92138d983185e661925e7be9e7457253 | Out of date |
| Grant-EpsEnginePermissions | 2.0.107 | 5fa791d09dc3068c9d0a3276281c0f545c8903189d580715d7749687c7544085 | Out of date |
| Grant-EpsHybridWorkerAccess | 2.0.107 | c4c115d536a91e3476fa8f515ebbc1788c904d5f67eb03f153942a1d666604c8 | Out of date |
| Grant-EpsUpdaterPermissions | 2.0.107 | c9e39f34b8c5157d6616a9762b34827a029d642e8325d8983a09781dbbb744df | Out of date |
| Import-EpsV1Data | 2.0.107 | 1cec1791c598795aef44700cf4d0a8efc89508a99f252e8b4aa3dd8be5e39235 | Out of date |
| Initialize-EpsHybridWorkerAd | 2.0.107 | a1814facb242131b3ce96d823d8ed27e4cb8e4ecc11b397aabf86e630f1d2105 | Out of date |
| Initialize-EpsLogAnalytics | 2.0.107 | de9c9671013bd168f42929ca4e3f9c46965cde6d84c87e7e943e06bb94449522 | Out of date |
| Initialize-EpsMailSender | 2.0.107 | 16338c80463d598a4cccf2ff04adcd18f3ab7f03cd407e6fd55fd75121993c5f | Out of date |
| Install-EpsHybridWorker | 2.0.107 | 774d7b427b26a4d0093ef917345642316baca4c1c19aa2a30f236051a39863a0 | Out of date |
| Set-EpsWebAccess | 2.0.107 | 463b67c29bffd5019447e850fda7aaf382af74c418bc31148ac1921eb21ec765 | Out of date |
| Start-EpsInstall | 2.0.107 | f93eb5d9ec76d281555a21089043917480bed01841592a6fcf6582a4114af875 | Out of date |
Download, read, run
Never pipe a script into Invoke-Expression. Download it to a file (line 1), read it, preview with -WhatIf (line 2), then run the same line without -WhatIf:
Invoke-WebRequest https://invardia.com/support/eps/<Script>.ps1 -OutFile <Script>.ps1
.\<Script>.ps1 -WhatIfTo check a download, compare its SHA-256 (Get-FileHash) with the publishedSHA256SUMS.txt or the table above.
Each script's page shows these commands with its own name. All scripts: Entra Policy Suite scripts · Every setting explained: Entra Policy Suite settings