Skip to content

Commit 36dc2bf

Browse files
committed
terraform/azure: skip Kconfig generation when Azure is not configured
Add require_azure_credentials() to azure_common.py which validates Azure credentials via the Azure CLI profile early in script execution. When Azure is not configured, the scripts exit gracefully with success rather than failing with errors. This allows users without Azure accounts to run make dynconfig without issues. This approach mirrors the AWS implementation and centralizes the handling of missing Azure credentials while avoiding TOCTOU race conditions from manual file existence checks. The Azure CLI profile API validates that credentials are functional, not just that config files exist. Generated-by: Claude AI Signed-off-by: Chuck Lever <[email protected]>
1 parent fd41bff commit 36dc2bf

File tree

4 files changed

+78
-6
lines changed

4 files changed

+78
-6
lines changed

terraform/azure/scripts/azure_common.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@
1717
from jinja2 import Environment, FileSystemLoader
1818

1919

20+
class AzureNotConfiguredError(Exception):
21+
"""Raised when Azure credentials are not available."""
22+
23+
pass
24+
25+
2026
def get_default_region():
2127
"""
2228
Get the default Azure region from Azure configuration.
@@ -371,3 +377,45 @@ def exit_on_empty_result(result, context, quiet=False):
371377
)
372378
print("Run 'az login' to authenticate with Azure.", file=sys.stderr)
373379
sys.exit(1)
380+
381+
382+
def require_azure_credentials():
383+
"""
384+
Require Azure credentials, raising an exception if not configured.
385+
386+
This function should be called early in main() to validate Azure
387+
credentials. If Azure is not configured, it raises AzureNotConfiguredError
388+
to let the caller decide how to handle it.
389+
390+
This centralizes the handling of missing Azure credentials and avoids
391+
TOCTOU race conditions from manual file existence checks.
392+
393+
Returns:
394+
str: Subscription ID if credentials are valid
395+
396+
Raises:
397+
AzureNotConfiguredError: If Azure credentials are not found
398+
"""
399+
try:
400+
from azure.common.credentials import get_cli_profile
401+
402+
profile = get_cli_profile()
403+
credentials, subscription_id, _ = profile.get_login_credentials(
404+
resource="https://management.azure.com"
405+
)
406+
return subscription_id
407+
except ImportError as e:
408+
raise AzureNotConfiguredError("Azure SDK not installed") from e
409+
except Exception as e:
410+
# Only treat as "not configured" if it looks like an auth/login issue
411+
error_msg = str(e).lower()
412+
auth_indicators = [
413+
"login",
414+
"logged in",
415+
"authenticate",
416+
"credential",
417+
"az login",
418+
]
419+
if any(phrase in error_msg for phrase in auth_indicators):
420+
raise AzureNotConfiguredError("Azure credentials not found") from e
421+
raise

terraform/azure/scripts/gen_kconfig_image

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,12 @@ from collections import defaultdict
5555
from functools import lru_cache
5656

5757
from azure_common import (
58+
AzureNotConfiguredError,
5859
get_default_region,
5960
get_jinja2_environment,
60-
get_all_regions,
6161
get_region_kconfig_name,
6262
get_all_offers_and_skus,
63+
require_azure_credentials,
6364
)
6465

6566

@@ -730,6 +731,14 @@ def main():
730731
output_publishers_raw(args.quiet)
731732
return
732733

734+
# Allow make dynconfig to succeed without Azure credentials
735+
try:
736+
require_azure_credentials()
737+
except AzureNotConfiguredError:
738+
if not args.quiet:
739+
print("Azure not configured - skipping (optional)", file=sys.stderr)
740+
sys.exit(0)
741+
733742
publishers = get_known_publishers()
734743

735744
# Filter to specific publisher if requested

terraform/azure/scripts/gen_kconfig_location

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@ import sys
2626
import argparse
2727

2828
from azure_common import (
29+
AzureNotConfiguredError,
2930
get_default_region,
3031
get_all_regions,
3132
get_jinja2_environment,
3233
get_region_kconfig_name,
33-
exit_on_empty_result,
34+
require_azure_credentials,
3435
)
3536

3637

@@ -191,11 +192,18 @@ def main():
191192
"""Main function to run the program."""
192193
args = parse_arguments()
193194

195+
# Allow make dynconfig to succeed without Azure credentials
196+
try:
197+
require_azure_credentials()
198+
except AzureNotConfiguredError:
199+
if not args.quiet:
200+
print("Azure not configured - skipping (optional)", file=sys.stderr)
201+
sys.exit(0)
202+
194203
if not args.quiet:
195204
print("Fetching list of all Azure regions...", file=sys.stderr)
196205

197206
regions = get_all_regions(args.quiet)
198-
exit_on_empty_result(regions, "Azure region query", args.quiet)
199207

200208
if args.regions:
201209
if args.format == "kconfig":

terraform/azure/scripts/gen_kconfig_size

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,12 @@ import os
3939
import yaml
4040

4141
from azure_common import (
42+
AzureNotConfiguredError,
4243
get_default_region,
4344
get_all_regions,
4445
get_jinja2_environment,
4546
get_vm_sizes_and_skus,
46-
exit_on_empty_result,
47+
require_azure_credentials,
4748
)
4849

4950

@@ -602,21 +603,27 @@ def main():
602603
"""Main function to run the program."""
603604
args = parse_arguments()
604605

606+
# Allow make dynconfig to succeed without Azure credentials
607+
try:
608+
require_azure_credentials()
609+
except AzureNotConfiguredError:
610+
if not args.quiet:
611+
print("Azure not configured - skipping (optional)", file=sys.stderr)
612+
sys.exit(0)
613+
605614
# Determine which regions to query
606615
if args.region:
607616
# Query specific region only
608617
regions = [args.region]
609618
elif args.all_regions:
610619
# Query all regions
611620
regions = get_all_regions(args.quiet)
612-
exit_on_empty_result(regions, "Azure region query", args.quiet)
613621
else:
614622
# Query default region only
615623
regions = [get_default_region()]
616624

617625
# Get VM sizes and capabilities in a single API call
618626
sizes, sku_capabilities = get_all_vm_sizes_and_capabilities(regions, args.quiet)
619-
exit_on_empty_result(sizes, "Azure VM size query", args.quiet)
620627

621628
if args.families:
622629
output_families_raw(sizes, args.quiet)

0 commit comments

Comments
 (0)