diff --git a/prowler/lib/cli/parser.py b/prowler/lib/cli/parser.py
index a9791758..a3b4205e 100644
--- a/prowler/lib/cli/parser.py
+++ b/prowler/lib/cli/parser.py
@@ -430,7 +430,7 @@ Detailed documentation at https://docs.prowler.cloud
azure_auth_modes_group.add_argument(
"--browser-auth",
action="store_true",
- help="Use browser authentication to log in against azure ",
+ help="Use browser authentication to log in against Azure, --tenant-id is required for this option",
)
azure_auth_modes_group.add_argument(
"--managed-identity-auth",
@@ -445,6 +445,12 @@ Detailed documentation at https://docs.prowler.cloud
default=[],
help="Azure subscription ids to be scanned by prowler",
)
+ azure_parser.add_argument(
+ "--tenant-id",
+ nargs="?",
+ default=None,
+ help="Azure Tenant ID to be used with --browser-auth option",
+ )
def __init_gcp_parser__(self):
"""Init the GCP Provider CLI parser"""
diff --git a/prowler/lib/outputs/html.py b/prowler/lib/outputs/html.py
index a25aacf1..9e11d31f 100644
--- a/prowler/lib/outputs/html.py
+++ b/prowler/lib/outputs/html.py
@@ -408,6 +408,14 @@ def get_azure_html_assessment_summary(audit_info):
for key, value in audit_info.identity.subscriptions.items():
intermediate = key + " : " + value
printed_subscriptions.append(intermediate)
+
+ # check if identity is str(coming from SP) or dict(coming from browser or)
+ if isinstance(audit_info.identity.identity_id, dict):
+ html_identity = audit_info.identity.identity_id.get(
+ "userPrincipalName", "Identity not found"
+ )
+ else:
+ html_identity = audit_info.identity.identity_id
return (
"""
@@ -447,7 +455,7 @@ def get_azure_html_assessment_summary(audit_info):
Azure Identity ID: """
- + audit_info.identity.identity_id
+ + html_identity
+ """
diff --git a/prowler/providers/azure/azure_provider.py b/prowler/providers/azure/azure_provider.py
index d65a00e0..9bd28a86 100644
--- a/prowler/providers/azure/azure_provider.py
+++ b/prowler/providers/azure/azure_provider.py
@@ -17,10 +17,11 @@ class Azure_Provider:
browser_auth: bool,
managed_entity_auth: bool,
subscription_ids: list,
+ tenant_id: str,
):
logger.info("Instantiating Azure Provider ...")
self.credentials = self.__set_credentials__(
- az_cli_auth, sp_env_auth, browser_auth, managed_entity_auth
+ az_cli_auth, sp_env_auth, browser_auth, managed_entity_auth, tenant_id
)
self.identity = self.__set_identity_info__(
self.credentials,
@@ -32,7 +33,7 @@ class Azure_Provider:
)
def __set_credentials__(
- self, az_cli_auth, sp_env_auth, browser_auth, managed_entity_auth
+ self, az_cli_auth, sp_env_auth, browser_auth, managed_entity_auth, tenant_id
):
# Browser auth creds cannot be set with DefaultAzureCredentials()
if not browser_auth:
@@ -59,7 +60,15 @@ class Azure_Provider:
)
sys.exit(1)
else:
- credentials = InteractiveBrowserCredential()
+ try:
+ print(tenant_id)
+ credentials = InteractiveBrowserCredential(tenant_id=tenant_id)
+ except Exception as error:
+ logger.critical("Failed to retrieve azure credentials")
+ logger.critical(
+ f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}] -- {error}"
+ )
+ sys.exit(1)
return credentials
@@ -122,8 +131,9 @@ class Azure_Provider:
"Trying to retrieve user information from AAD to populate identity structure ..."
)
client = GraphClient(credential=credentials)
- user_name = client.get("/me").json()["userPrincipalName"]
- identity.identity_id = user_name
+ user_name = client.get("/me").json()
+ if "userPrincipalName" in user_name:
+ identity.identity_id = user_name
except Exception as error:
logger.error(
diff --git a/prowler/providers/common/audit_info.py b/prowler/providers/common/audit_info.py
index c1db60f3..27a06412 100644
--- a/prowler/providers/common/audit_info.py
+++ b/prowler/providers/common/audit_info.py
@@ -265,6 +265,7 @@ Azure Identity Type: {Fore.YELLOW}[{audit_info.identity.identity_type}]{Style.RE
sp_env_auth = arguments.get("sp_env_auth")
browser_auth = arguments.get("browser_auth")
managed_entity_auth = arguments.get("managed_entity_auth")
+ tenant_id = arguments.get("tenant_id")
if (
not az_cli_auth
and not sp_env_auth
@@ -274,6 +275,10 @@ Azure Identity Type: {Fore.YELLOW}[{audit_info.identity.identity_type}]{Style.RE
raise Exception(
"Azure provider requires at least one authentication method set: [--az-cli-auth | --sp-env-auth | --browser-auth | --managed-identity-auth]"
)
+ if (not browser_auth and tenant_id) or (browser_auth and not tenant_id):
+ raise Exception(
+ "Azure Tenant ID is required only for browser authentication mode"
+ )
azure_provider = Azure_Provider(
az_cli_auth,
@@ -281,6 +286,7 @@ Azure Identity Type: {Fore.YELLOW}[{audit_info.identity.identity_type}]{Style.RE
browser_auth,
managed_entity_auth,
subscription_ids,
+ tenant_id,
)
azure_audit_info.credentials = azure_provider.get_credentials()
azure_audit_info.identity = azure_provider.get_identity()
diff --git a/tests/lib/cli/parser_test.py b/tests/lib/cli/parser_test.py
index 3d9efa06..eadd2387 100644
--- a/tests/lib/cli/parser_test.py
+++ b/tests/lib/cli/parser_test.py
@@ -924,6 +924,14 @@ class Test_Parser:
assert parsed.provider == "azure"
assert parsed.browser_auth
+ def test_parser_azure_tenant_id(self):
+ argument = "--tenant-id"
+ tenant_id = "test-tenant-id"
+ command = [prowler_command, "azure", argument, tenant_id]
+ parsed = self.parser.parse(command)
+ assert parsed.provider == "azure"
+ assert parsed.tenant_id == tenant_id
+
def test_parser_azure_auth_az_cli(self):
argument = "--az-cli-auth"
command = [prowler_command, "azure", argument]