feat(azure): clarify certificate onboarding permissions and trim wizard copy

This commit is contained in:
Lydia Vilchez committed 2026-08-24 17:55:28 +02:00
1 parent 844f766c8f
commit 0f778b0862
4 files changed
+67 -30

No files matched your search

@@ -231,12 +231,43 @@ Certificate authentication is the recommended way to run Prowler against Azure b
### Prerequisites
Certificate authentication requires:
Certificate authentication touches two separate permission systems: **Microsoft Entra ID** (identity) and **Azure RBAC** (subscription). Both are required.
- Permission to create or manage a Microsoft Entra App Registration.
- An administrator authorized to grant tenant-wide consent for the required Microsoft Graph application permissions.
- `Owner` on each target subscription when using the deployment template. `Contributor` cannot create role definitions or role assignments.
- The Service Principal Object ID from **Microsoft Entra ID** > **Enterprise applications** > the application > **Overview**. This value differs from the Object ID under **App registrations**.
#### Microsoft Entra ID roles per setup step
| Setup step | Minimum role required |
| ----------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **1. Register a new App Registration** | Any member user, _by default_. Member users can register applications through the built-in default user permissions. If the tenant disabled this by setting _Users can register applications_ to **No** (under **Microsoft Entra ID** > **Users** > **User settings**), the user needs one of: `Application Administrator`, `Cloud Application Administrator`, or `Global Administrator`. |
| **2. Upload the certificate to the App Registration** | The App Registration's owner (the user who created it, added automatically) — or `Application Administrator`, `Cloud Application Administrator`, or `Global Administrator`. |
| **3. Add Microsoft Graph _application_ permissions (request)** — `AuditLog.Read.All`, `Directory.Read.All`, `Policy.Read.All` | The App Registration's owner — or `Application Administrator`, `Cloud Application Administrator`, or `Global Administrator`. Requesting a permission and granting consent are separate operations; see step 4 for the consent role. |
| **4. Grant tenant-wide admin consent for those permissions** ⚠️ | **`Global Administrator`** or **`Privileged Role Administrator`** _only_. `Application Administrator`, `Cloud Application Administrator`, and `AI Administrator` **cannot** consent to _Microsoft Graph_ application permissions — they can consent to application permissions for other APIs, but not Microsoft Graph app roles, per [Microsoft's admin consent documentation](https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/grant-admin-consent#prerequisites) and [Microsoft Graph permissions overview](https://learn.microsoft.com/en-us/graph/permissions-overview). |
| **5. Read the Service Principal Object ID from Enterprise applications** | Any signed-in user (read-only, granted by default user permissions). |
The Service Principal Object ID lives on **Microsoft Entra ID** > **Enterprise applications** > _the application_ > **Overview**. This value is different from the Object ID that appears under **App registrations** for the same application; both objects share the same Application (client) ID but each has its own Object ID.
#### Azure RBAC role for the subscription template
The Deploy to Azure template creates one custom role definition and two role assignments (`Reader` and the custom `ProwlerRole`) at subscription scope. The account that runs the deployment needs one of:
- **`Owner`** on the target subscription. Simplest and most common. `Owner` grants both `Microsoft.Authorization/roleDefinitions/write` and `Microsoft.Authorization/roleAssignments/write`.
- Alternatively, **`Contributor` combined with `User Access Administrator`** on the target subscription. `Contributor` alone is not enough because it explicitly cannot create role assignments (see [Azure built-in roles](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles)); `User Access Administrator` fills the gap for role definitions and role assignments.
<Note>
**Global Administrator does not automatically have Azure RBAC.** Per
[Microsoft's Elevate access
documentation](https://learn.microsoft.com/en-us/azure/role-based-access-control/elevate-access-global-admin):
"As a Global Administrator in Microsoft Entra ID, you might not have access to
all subscriptions and management groups in your tenant." To grant a Global
Administrator temporary access, open **Microsoft Entra ID** > **Properties**
and set **Access management for Azure resources** to **Yes**. This assigns
`User Access Administrator` at root scope (`/`), which covers step 6. Disable
the toggle after the setup is done — the assignment persists per-user until
revoked.
</Note>
#### Shortest single-user path
The simplest way for one user to complete the whole flow is **`Global Administrator`** in Microsoft Entra ID (covers steps 1-4 by inclusion) combined with the Elevate access toggle above (covers step 6). Deploying with a least-privilege split (for example, an `Application Administrator` for step 1-3, a `Privileged Role Administrator` for step 4, and an `Owner` for step 6) is also supported.
### Generating the Certificate
Binary file not shown.
@@ -103,22 +103,22 @@ describe("AzureCertificateCredentialsForm", () => {
const steps = screen.getAllByRole("listitem");
expect(steps).toHaveLength(6);
expect(steps[0]).toHaveTextContent(
"Register an Azure application, then copy its Directory (tenant) ID and Application (client) ID.",
"Register an Azure application. From its Overview page, copy the Directory (tenant) ID and Application (client) ID.",
);
expect(steps[1]).toHaveTextContent(
"Generate the certificate. The private bundle is filled below and submitted to Prowler only when you connect.",
"Generate a certificate. The private bundle fills the field below and prowler-cert.cer downloads for step 3.",
);
expect(steps[2]).toHaveTextContent(
"Upload prowler-cert.cer under Certificates & secrets. Add Microsoft Graph application permissions: AuditLog.Read.All, Directory.Read.All (or Domain.Read.All), and Policy.Read.All. Then grant admin consent.",
"In your App Registration, upload prowler-cert.cer under Certificates & secrets. Then in API permissions, add the Microsoft Graph application permissions AuditLog.Read.All, Directory.Read.All, and Policy.Read.All, and click Grant admin consent.",
);
expect(steps[3]).toHaveTextContent(
"Copy the Service Principal Object ID from Enterprise applications.",
"Open Enterprise applications, select the same app, and copy its Object ID (different from the App Registration's Object ID).",
);
expect(steps[4]).toHaveTextContent(
"Deploy subscription RBAC with the Service Principal Object ID. The template creates ProwlerRole and assigns Reader and ProwlerRole; it does not create Entra resources.",
"Deploy the template with the Service Principal Object ID from step 4. It grants Reader and a custom ProwlerRole on the subscription.",
);
expect(steps[5]).toHaveTextContent(
"Return to Prowler and complete the fields below. The generated certificate bundle is already filled.",
"Return to Prowler and paste the Tenant ID and Client ID from step 1 into the fields below. The certificate field is already filled.",
);
expect(
screen.getByRole("textbox", {
@@ -73,8 +73,10 @@ export const AzureCertificateCredentialsForm = ({
Certificate Authentication (Recommended)
</div>
<div className="text-text-neutral-tertiary text-xs">
Requires permission to create App Registrations and grant Microsoft
Graph admin consent, plus <strong>Owner</strong> on the subscription.{" "}
Requires <strong>Global Administrator</strong> or{" "}
<strong>Privileged Role Administrator</strong> in Microsoft Entra ID
(for the admin consent in step 3), plus <strong>Owner</strong> on the
target subscription (step 5).{" "}
<Link
href={DOCS_CERT_GENERATION_URL}
target="_blank"
@@ -97,8 +99,8 @@ export const AzureCertificateCredentialsForm = ({
<li>
<div className="flex flex-col items-start gap-2 sm:flex-row sm:items-center sm:justify-between">
<span>
Register an Azure application, then copy its Directory
(tenant) ID and Application (client) ID.
Register an Azure application. From its Overview page, copy
the Directory (tenant) ID and Application (client) ID.
</span>
<Button variant="link" size="link-sm" asChild>
<a
@@ -115,8 +117,8 @@ export const AzureCertificateCredentialsForm = ({
<li>
<div className="flex flex-col items-start gap-2 sm:flex-row sm:items-center sm:justify-between">
<span>
Generate the certificate. The private bundle is filled below
and submitted to Prowler only when you connect.
Generate a certificate. The private bundle fills the field
below and <code>prowler-cert.cer</code> downloads for step 3.
</span>
<Button
type="button"
@@ -159,13 +161,16 @@ export const AzureCertificateCredentialsForm = ({
className="flex list-decimal flex-col gap-3 pl-5 text-sm"
>
<li>
Upload <code>prowler-cert.cer</code> under Certificates &amp;
secrets. Add Microsoft Graph application permissions:
AuditLog.Read.All, Directory.Read.All (or Domain.Read.All), and
Policy.Read.All. Then grant admin consent.
In your App Registration, upload <code>prowler-cert.cer</code>{" "}
under <em>Certificates &amp; secrets</em>. Then in{" "}
<em>API permissions</em>, add the Microsoft Graph{" "}
<strong>application</strong> permissions{" "}
<code>AuditLog.Read.All</code>, <code>Directory.Read.All</code>,
and <code>Policy.Read.All</code>, and click{" "}
<em>Grant admin consent</em>.
</li>
<li>
Copy the Service Principal Object ID from{" "}
Open{" "}
<Link
href={AZURE_PORTAL_ENTERPRISE_APPLICATIONS_URL}
target="_blank"
@@ -175,7 +180,8 @@ export const AzureCertificateCredentialsForm = ({
<ExternalLink className="size-3.5 shrink-0" />
<span>Enterprise applications</span>
</Link>
.
, select the same app, and copy its Object ID (different from the
App Registration&apos;s Object ID).
</li>
</ol>
</section>
@@ -191,9 +197,9 @@ export const AzureCertificateCredentialsForm = ({
<li>
<div className="flex flex-col items-start gap-2 sm:flex-row sm:items-center sm:justify-between">
<span>
Deploy subscription RBAC with the Service Principal Object ID.
The template creates ProwlerRole and assigns Reader and
ProwlerRole; it does not create Entra resources.
Deploy the template with the Service Principal Object ID from
step 4. It grants Reader and a custom ProwlerRole on the
subscription.
</span>
<Button variant="default" size="sm" asChild>
<a
@@ -222,8 +228,8 @@ export const AzureCertificateCredentialsForm = ({
</p>
</li>
<li>
Return to Prowler and complete the fields below. The generated
certificate bundle is already filled.
Return to Prowler and paste the Tenant ID and Client ID from step
1 into the fields below. The certificate field is already filled.
</li>
</ol>
</section>
@@ -235,7 +241,7 @@ export const AzureCertificateCredentialsForm = ({
type="text"
label="Tenant ID"
labelPlacement="inside"
placeholder="Enter the Tenant ID"
placeholder="Directory (tenant) ID"
variant="bordered"
isRequired
/>
@@ -245,7 +251,7 @@ export const AzureCertificateCredentialsForm = ({
type="text"
label="Client ID"
labelPlacement="inside"
placeholder="Enter the Client ID"
placeholder="Application (client) ID"
variant="bordered"
isRequired
/>