Back to Checks
azure

Function app managed identity is not assigned Owner, Contributor, User Access Administrator, or Role Based Access Control Administrator roles

app_function_identity_without_admin_privileges
Service: appSeverity: high
OfficialBuilt-inVerified
Categories:identity-access
Resource Type:microsoft.web/sites

Azure Function apps with managed identities are evaluated for assignments to broad administrative roles: Owner, Contributor, User Access Administrator, RBAC Administrator.

The finding highlights functions whose identity carries elevated permissions beyond normal runtime needs.

Risk

Admin rights on a function's identity expose the control plane.

  • Confidentiality: read secrets and data
  • Integrity: alter configs, grant roles, deploy changes
  • Availability: stop or delete resources A runtime compromise can enable lateral movement and privilege escalation across the environment.

Run this check with Prowler

Run in Prowler Cloud
Run
prowler azure --checks app_function_identity_without_admin_privileges

Recommendation

Apply least privilege: grant only narrowly scoped, data-plane permissions needed by the function; avoid broad roles like Owner or Contributor. Use separation of duties and just-in-time elevation for rare admin tasks. Regularly review role assignments and restrict scope to the smallest necessary boundary.

Remediation

CLI

az role assignment delete --assignee <MANAGED_IDENTITY_PRINCIPAL_ID> --scope <SCOPE_ID>

Other

1In the Azure portal, open the scope where the role is assigned (e.g., Subscription, Resource group, or the Function App resource)
2Go to Access control (IAM) > Role assignments
3In the Principal filter, search for the Function App's managed identity (<function-app-name>)
4For each assignment with role Owner, Contributor, User Access Administrator, or Role Based Access Control Administrator, click Remove
5Repeat steps 1-4 at all relevant scopes (subscription, resource group, and Function App) until no such admin roles remain for this identity

Check Code

from prowler.lib.check.models import Check, Check_Report_Azure
from prowler.providers.azure.config import (
    CONTRIBUTOR_ROLE_ID,
    OWNER_ROLE_ID,
    ROLE_BASED_ACCESS_CONTROL_ADMINISTRATOR_ROLE_ID,
    USER_ACCESS_ADMINISTRATOR_ROLE_ID,
)
from prowler.providers.azure.services.app.app_client import app_client
from prowler.providers.azure.services.iam.iam_client import iam_client


class app_function_identity_without_admin_privileges(Check):
    def execute(self):
        findings = []

        for (
            subscription_id,
            functions,
        ) in app_client.functions.items():
            subscription_name = app_client.subscriptions.get(
                subscription_id, subscription_id
            )
            for function in functions.values():
                if function.identity:
                    report = Check_Report_Azure(
                        metadata=self.metadata(), resource=function
                    )
                    report.subscription = subscription_id
                    report.status = "PASS"
                    report.status_extended = f"Function {function.name} from subscription {subscription_name} ({subscription_id}) has a managed identity enabled but without admin privileges."

                    admin_roles_assigned = []

                    for role_assignment in iam_client.role_assignments[
                        subscription_id
                    ].values():
                        if (
                            role_assignment.agent_id == function.identity.principal_id
                            and role_assignment.role_id
                            in [
                                CONTRIBUTOR_ROLE_ID,
                                OWNER_ROLE_ID,
                                ROLE_BASED_ACCESS_CONTROL_ADMINISTRATOR_ROLE_ID,
                                USER_ACCESS_ADMINISTRATOR_ROLE_ID,
                            ]
                        ):
                            admin_roles_assigned.append(
                                getattr(
                                    iam_client.roles[subscription_id].get(
                                        f"/subscriptions/{subscription_id}/providers/Microsoft.Authorization/roleDefinitions/{role_assignment.role_id}"
                                    ),
                                    "name",
                                    "",
                                )
                            )

                    if admin_roles_assigned:
                        report.status = "FAIL"
                        report.status_extended = f"Function {function.name} from subscription {subscription_name} ({subscription_id}) has a managed identity enabled and it is configure with admin privileges using {'roles: ' + ', '.join(admin_roles_assigned) if len(admin_roles_assigned) > 1 else 'role ' + admin_roles_assigned[0]}."

                    findings.append(report)

        return findings