feat(app): split SDK App service calls (#7778)

This commit is contained in:
Rubén De la Torre Vico
2025-05-27 09:52:50 +02:00
committed by GitHub
parent 66acfd8691
commit f254a4bc0d
5 changed files with 95 additions and 64 deletions

View File

@@ -81,6 +81,9 @@ Prowler for Azure needs two types of permission scopes to be set:
To assign the permissions, follow the instructions in the [Microsoft Entra ID permissions](../tutorials/azure/create-prowler-service-principal.md#assigning-the-proper-permissions) section and the [Azure subscriptions permissions](../tutorials/azure/subscriptions.md#assign-the-appropriate-permissions-to-the-identity-that-is-going-to-be-assumed-by-prowler) section, respectively.
???+ warning
Some permissions in `ProwlerRole` are considered **write** permissions, so if you have a `ReadOnly` lock attached to some resources you may get an error and will not get a finding for that check.
#### Checks that require ProwlerRole
The following checks require the `ProwlerRole` permissions to be executed, if you want to run them, make sure you have assigned the role to the identity that is going to be assumed by Prowler:

View File

@@ -11,19 +11,20 @@ class app_function_access_keys_configured(Check):
functions,
) in app_client.functions.items():
for function in functions.values():
report = Check_Report_Azure(metadata=self.metadata(), resource=function)
report.subscription = subscription_name
report.status = "FAIL"
report.status_extended = (
f"Function {function.name} does not have function keys configured."
)
if len(function.function_keys) > 0:
report.status = "PASS"
report.status_extended = (
f"Function {function.name} has function keys configured."
if function.function_keys is not None:
report = Check_Report_Azure(
metadata=self.metadata(), resource=function
)
report.subscription = subscription_name
report.status = "FAIL"
report.status_extended = f"Function {function.name} does not have function keys configured."
findings.append(report)
if len(function.function_keys) > 0:
report.status = "PASS"
report.status_extended = (
f"Function {function.name} has function keys configured."
)
findings.append(report)
return findings

View File

@@ -14,26 +14,29 @@ class app_function_application_insights_enabled(Check):
functions,
) in app_client.functions.items():
for function in functions.values():
report = Check_Report_Azure(metadata=self.metadata(), resource=function)
report.subscription = subscription_name
report.status = "FAIL"
report.status_extended = (
f"Function {function.name} is not using Application Insights."
)
if function.enviroment_variables.get(
"APPINSIGHTS_INSTRUMENTATIONKEY", ""
) in [
component.instrumentation_key
for component in appinsights_client.components[
subscription_name
].values()
]:
report.status = "PASS"
if function.enviroment_variables is not None:
report = Check_Report_Azure(
metadata=self.metadata(), resource=function
)
report.subscription = subscription_name
report.status = "FAIL"
report.status_extended = (
f"Function {function.name} is using Application Insights."
f"Function {function.name} is not using Application Insights."
)
findings.append(report)
if function.enviroment_variables.get(
"APPINSIGHTS_INSTRUMENTATIONKEY", ""
) in [
component.instrumentation_key
for component in appinsights_client.components[
subscription_name
].values()
]:
report.status = "PASS"
report.status_extended = (
f"Function {function.name} is using Application Insights."
)
findings.append(report)
return findings

View File

@@ -11,20 +11,25 @@ class app_function_latest_runtime_version(Check):
functions,
) in app_client.functions.items():
for function in functions.values():
report = Check_Report_Azure(metadata=self.metadata(), resource=function)
report.subscription = subscription_name
report.status = "PASS"
report.status_extended = (
f"Function {function.name} is using the latest runtime."
)
if function.enviroment_variables is not None:
report = Check_Report_Azure(
metadata=self.metadata(), resource=function
)
report.subscription = subscription_name
report.status = "PASS"
report.status_extended = (
f"Function {function.name} is using the latest runtime."
)
if (
function.enviroment_variables.get("FUNCTIONS_EXTENSION_VERSION", "")
!= "~4"
):
report.status = "FAIL"
report.status_extended = f"Function {function.name} is not using the latest runtime. The current runtime is '{function.enviroment_variables.get('FUNCTIONS_EXTENSION_VERSION', '')}' and should be '~4'."
if (
function.enviroment_variables.get(
"FUNCTIONS_EXTENSION_VERSION", ""
)
!= "~4"
):
report.status = "FAIL"
report.status_extended = f"Function {function.name} is not using the latest runtime. The current runtime is '{function.enviroment_variables.get('FUNCTIONS_EXTENSION_VERSION', '')}' and should be '~4'."
findings.append(report)
findings.append(report)
return findings

View File

@@ -1,5 +1,5 @@
from dataclasses import dataclass, field
from typing import Dict, List
from typing import Dict, List, Optional
from azure.mgmt.web import WebSiteManagementClient
@@ -124,14 +124,16 @@ class App(AzureService):
# Filter function apps
if getattr(function, "kind", "").startswith("functionapp"):
# List host keys
host_keys = client.web_apps.list_host_keys(
resource_group_name=function.resource_group,
name=function.name,
) # Need to add role 'Logic App Contributor' to the service principal to get the host keys or add to the reader role the permission 'Microsoft.Web/sites/host/listkeys'
host_keys = self._get_function_host_keys(
subscription_name, function.resource_group, function.name
)
if host_keys is not None:
function_keys = getattr(host_keys, "function_keys", {})
else:
function_keys = None
function_config = client.web_apps.get_configuration(
resource_group_name=function.resource_group,
name=function.name,
function_config = self._get_function_config(
subscription_name, function.resource_group, function.name
)
functions[subscription_name].update(
@@ -141,16 +143,9 @@ class App(AzureService):
name=function.name,
location=function.location,
kind=function.kind,
function_keys=getattr(
host_keys, "function_keys", {}
),
function_keys=function_keys,
enviroment_variables=getattr(
client.web_apps.list_application_settings(
resource_group_name=function.resource_group,
name=function.name,
),
"properties",
{},
function_config, "properties", None
),
identity=getattr(function, "identity", None),
public_access=(
@@ -167,7 +162,7 @@ class App(AzureService):
"",
),
ftps_state=getattr(
function_config, "ftps_state", ""
function_config, "ftps_state", None
),
)
}
@@ -209,6 +204,30 @@ class App(AzureService):
)
return monitor_diagnostics_settings
def _get_function_host_keys(self, subscription, resource_group, name):
try:
return self.clients[subscription].web_apps.list_host_keys(
resource_group_name=resource_group,
name=name,
)
except Exception as error:
logger.error(
f"Error getting host keys for {name} in {resource_group}: {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
return None
def _get_function_config(self, subscription, resource_group, name):
try:
return self.clients[subscription].web_apps.list_application_settings(
resource_group_name=resource_group,
name=name,
)
except Exception as error:
logger.error(
f"Error getting configuration for {name} in {resource_group}: {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
return None
@dataclass
class ManagedServiceIdentity:
@@ -250,9 +269,9 @@ class FunctionApp:
name: str
location: str
kind: str
function_keys: Dict[str, str]
enviroment_variables: Dict[str, str]
function_keys: Optional[Dict[str, str]]
enviroment_variables: Optional[Dict[str, str]]
identity: ManagedServiceIdentity
public_access: bool
vnet_subnet_id: str
ftps_state: str
ftps_state: Optional[str]