diff --git a/ui/components/providers/workflow/forms/select-credentials-type/azure/credentials-type/azure-certificate-credentials-form.integration.test.tsx b/ui/components/providers/workflow/forms/select-credentials-type/azure/credentials-type/azure-certificate-credentials-form.integration.test.tsx
index 37e1594d73..f7300ec5fd 100644
--- a/ui/components/providers/workflow/forms/select-credentials-type/azure/credentials-type/azure-certificate-credentials-form.integration.test.tsx
+++ b/ui/components/providers/workflow/forms/select-credentials-type/azure/credentials-type/azure-certificate-credentials-form.integration.test.tsx
@@ -17,6 +17,57 @@ const Harness = () => {
};
describe("AzureCertificateCredentialsForm browser flow", () => {
+ it("groups the six setup steps into three named phases before the form", async () => {
+ // When
+ const view = await render( );
+
+ // Then
+ const phaseHeadings = [
+ view.getByRole("heading", { name: "Create the application" }).element(),
+ view.getByRole("heading", { name: "Configure access" }).element(),
+ view.getByRole("heading", { name: "Deploy and connect" }).element(),
+ ];
+ const phases = phaseHeadings.map((heading) => heading.closest("section"));
+
+ expect(phases.every((phase) => phase !== null)).toBe(true);
+ expect(phases.map((phase) => phase?.querySelectorAll("li").length)).toEqual(
+ [2, 2, 2],
+ );
+ const openAzureLink = view
+ .getByRole("link", { name: "Open Azure" })
+ .element();
+ const enterpriseApplicationsLink = view
+ .getByRole("link", { name: "Enterprise applications" })
+ .element();
+ const deployToAzureLink = view
+ .getByRole("link", { name: "Deploy to Azure" })
+ .element();
+ const openTemplateLink = view
+ .getByRole("link", { name: "Open template" })
+ .element();
+
+ expect(phases[0]).toContainElement(openAzureLink);
+ expect(phases[0]).toContainElement(
+ view.getByRole("button", { name: "Generate certificate" }).element(),
+ );
+ expect(phases[1]).toContainElement(enterpriseApplicationsLink);
+ expect(phases[2]).toContainElement(deployToAzureLink);
+ expect(deployToAzureLink.closest("li")).toContainElement(openTemplateLink);
+ expect(openAzureLink).toHaveAttribute(
+ "href",
+ "https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/CreateApplicationBlade",
+ );
+ expect(enterpriseApplicationsLink).toHaveAttribute(
+ "href",
+ "https://portal.azure.com/#view/Microsoft_AAD_IAM/StartboardApplicationsMenuBlade/~/AppAppsPreview",
+ );
+ const guidance = phaseHeadings[0].parentElement?.parentElement;
+ const tenantIdInput = view
+ .getByRole("textbox", { name: "Tenant ID" })
+ .element();
+ expect(guidance?.nextElementSibling).toContainElement(tenantIdInput);
+ });
+
it("renders deployment guidance and keeps certificate generation functional", async () => {
// When
const view = await render( );
@@ -45,6 +96,9 @@ describe("AzureCertificateCredentialsForm browser flow", () => {
},
{ timeout: 20_000 },
);
+ expect(view.getByRole("status").element()).toHaveTextContent(
+ /Downloaded prowler-cert\.cer/,
+ );
const bundle = view
.getByRole("textbox", {
name: "Certificate and Private Key Bundle (Base64)",
diff --git a/ui/components/providers/workflow/forms/select-credentials-type/azure/credentials-type/azure-certificate-credentials-form.tsx b/ui/components/providers/workflow/forms/select-credentials-type/azure/credentials-type/azure-certificate-credentials-form.tsx
index aaecac6d2b..c087e209f8 100644
--- a/ui/components/providers/workflow/forms/select-credentials-type/azure/credentials-type/azure-certificate-credentials-form.tsx
+++ b/ui/components/providers/workflow/forms/select-credentials-type/azure/credentials-type/azure-certificate-credentials-form.tsx
@@ -88,115 +88,146 @@ export const AzureCertificateCredentialsForm = ({
-
-
-
- 1. Create an App Registration. Copy its Directory
- (tenant) ID and Application (client) ID.
-
-
-
-
- New App Registration
-
-
-
+
+
+
+ Create the application
+
+
+
+
+
+ Register an Azure application, then copy its Directory
+ (tenant) ID and Application (client) ID.
+
+
+
+
+ Open Azure
+
+
+
+
+
+
+
+ Generate the certificate. The private bundle is filled below
+ and submitted to Prowler only when you connect.
+
+
+ {isGeneratingCert ? "Generating..." : "Generate certificate"}
+
+
+
+ {isGeneratingCert && (
+
+ Generating certificate...
+
+ )}
+ {generatorError && (
+
+ {generatorError}
+
+ )}
+ {generatedThumbprint && (
+
+ Downloaded prowler-cert.cer. Thumbprint{" "}
+ {generatedThumbprint}
+
+ )}
+
+
+
+
-
-
-
- 2. Generate the certificate bundle and upload the
- public prowler-cert.cer file under Certificates & secrets. The
- private bundle is filled below and is submitted to Prowler only
- when you connect.
-
-
- {isGeneratingCert ? "Generating..." : "Generate certificate"}
-
-
- {generatorError && (
- {generatorError}
- )}
- {generatedThumbprint && (
-
- Downloaded prowler-cert.cer. Thumbprint{" "}
- {generatedThumbprint}
-
- )}
-
-
-
- 3. Add Microsoft Graph application permissions:
- AuditLog.Read.All, Directory.Read.All (or Domain.Read.All), and
- Policy.Read.All. Then grant admin consent.
-
-
-
-
- 4. Copy the Service Principal Object ID from{" "}
-
-
- Enterprise applications
-
- .
-
-
-
-
-
-
- To deploy the ARM JSON manually, use Azure Portal → Build your own
- template in the editor.{" "}
-
-
-
- Open template
-
-
-
-
+
Enterprise applications
+
+ .
+
+
+
-
- 6. Return to Prowler and connect. Paste the Tenant ID
- and Application Client ID below; the generated certificate bundle is
- already filled.
-
-
+
+
+ Deploy and connect
+
+
+
+
+
+ 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 to Azure
+
+
+
+
+ Manual deployment: in Azure Portal, use Build your own template
+ in the editor.{" "}
+
+
+
+ Open template
+
+
+
+
+
+ Return to Prowler and complete the fields below. The generated
+ certificate bundle is already filled.
+
+
+
+