|
| 1 | +""" |
| 2 | +Simple client example showing the methods for calling Azure Function App endpoints |
| 3 | +
|
| 4 | +IMPORTANT: The what-if service requires client-side authentication to operate under the |
| 5 | +caller's subscription and permissions. Server-side authentication is not supported for |
| 6 | +what-if operations as it would not provide access to the caller's subscription. |
| 7 | +
|
| 8 | +This client now uses DefaultAzureCredential which supports multiple authentication methods: |
| 9 | +- Azure CLI: az login |
| 10 | +- Environment variables (service principal): AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_TENANT_ID |
| 11 | +- Managed Identity (when running in Azure environments) |
| 12 | +- Visual Studio/VS Code authentication |
| 13 | + |
| 14 | +The what-if service will use your configured credentials to access your subscription |
| 15 | +and preview deployment changes under your permissions. |
| 16 | +""" |
| 17 | + |
| 18 | +import requests |
| 19 | +import json |
| 20 | +from typing import Dict, Any, Optional |
| 21 | +from azure.identity import DefaultAzureCredential |
| 22 | +from datetime import datetime, timezone |
| 23 | +from azure.cli.core.util import send_raw_request |
| 24 | + |
| 25 | + |
| 26 | +# Configuration |
| 27 | +FUNCTION_APP_URL = "https://azcli-script-insight.azurewebsites.net" |
| 28 | + |
| 29 | +def translate_cli_to_bicep(function_app_url: str, azcli_script: str) -> Dict[str, Any]: |
| 30 | + """ |
| 31 | + Translate Azure CLI script to Bicep template |
| 32 | + |
| 33 | + Args: |
| 34 | + function_app_url: Base URL of your Azure Function App |
| 35 | + azcli_script: Azure CLI script to translate |
| 36 | + |
| 37 | + Returns: |
| 38 | + Dictionary with translation result |
| 39 | + """ |
| 40 | + url = f"{function_app_url.rstrip('/')}/api/cli_to_bicep" |
| 41 | + |
| 42 | + headers = { |
| 43 | + 'Content-Type': 'application/json', |
| 44 | + 'Accept': 'application/json' |
| 45 | + } |
| 46 | + |
| 47 | + payload = {"azcli_script": azcli_script} |
| 48 | + |
| 49 | + try: |
| 50 | + response = requests.post(url, json=payload, headers=headers, timeout=300) |
| 51 | + return response.json() |
| 52 | + except requests.RequestException as e: |
| 53 | + return {"error": str(e), "success": False} |
| 54 | + |
| 55 | + |
| 56 | +def what_if_preview(cli_ctx, function_app_url: str, azcli_script: str, subscription_id: Optional[str] = None) -> Dict[str, Any]: |
| 57 | + """ |
| 58 | + Preview deployment changes using Azure what-if functionality |
| 59 | + |
| 60 | + Args: |
| 61 | + function_app_url: Base URL of your Azure Function App |
| 62 | + azcli_script: Azure CLI script to analyze |
| 63 | + subscription_id: Optional fallback subscription ID if not in script |
| 64 | + |
| 65 | + Returns: |
| 66 | + Dictionary with what-if preview result |
| 67 | + """ |
| 68 | + url = f"{function_app_url.rstrip('/')}/api/what_if_preview" |
| 69 | + |
| 70 | + headers = { |
| 71 | + 'Content-Type': 'application/json', |
| 72 | + 'Accept': 'application/json' |
| 73 | + } |
| 74 | + |
| 75 | + payload = {"azcli_script": azcli_script} |
| 76 | + if subscription_id: |
| 77 | + payload["subscription_id"] = subscription_id |
| 78 | + |
| 79 | + try: |
| 80 | + response = send_raw_request(cli_ctx, "POST", url, body=json.dumps(payload), resource="https://management.azure.com") |
| 81 | + return response.json() |
| 82 | + except requests.RequestException as e: |
| 83 | + return {"error": str(e), "success": False} |
| 84 | + |
| 85 | + |
| 86 | +def analyze_azcli_script(function_app_url: str, azcli_script: str) -> Dict[str, Any]: |
| 87 | + """ |
| 88 | + Analyze Azure CLI script for best practices and recommendations |
| 89 | + |
| 90 | + Args: |
| 91 | + function_app_url: Base URL of your Azure Function App |
| 92 | + azcli_script: Azure CLI script to analyze |
| 93 | + |
| 94 | + Returns: |
| 95 | + Dictionary with analysis result |
| 96 | + """ |
| 97 | + url = f"{function_app_url.rstrip('/')}/api/analyze_azcli_script" |
| 98 | + |
| 99 | + headers = { |
| 100 | + 'Content-Type': 'application/json', |
| 101 | + 'Accept': 'application/json' |
| 102 | + } |
| 103 | + |
| 104 | + payload = {"azcli_script": azcli_script} |
| 105 | + |
| 106 | + try: |
| 107 | + response = requests.post(url, json=payload, headers=headers, timeout=30) |
| 108 | + return response.json() |
| 109 | + except requests.RequestException as e: |
| 110 | + return {"error": str(e), "status": "error"} |
| 111 | + |
| 112 | + |
| 113 | +# Example usage |
| 114 | +if __name__ == "__main__": |
| 115 | + |
| 116 | + # Sample Azure CLI script |
| 117 | + sample_script = "# Create a resource group with uppercase name \n az group create --name azcli-script-insight --location eastus \n \n # Create a VM directly instead of using an ARM template \n az vm create --resource-group azcli-script-insight --name MyVM_01 --image UbuntuLTS --size Standard_D2s_v3 --admin-username azureuser --generate-ssh-keys \n \n # Create a VMSS without auto-scaling \n az vmss create --resource-group azcli-script-insight --name MyVMSS --image UbuntuLTS --instance-count 3 --admin-username azureuser --generate-ssh-keys \n # Create a web app without managed identity \n az webapp create --resource-group azcli-script-insight --plan MyAppServicePlan --name MyWebApp \n \n # Create duplicate resource group (redundant) \n az group create --name azcli-script-insight --location eastus \n \n # Loop through VMs and query details individually (inefficient) \n for vm in $(az vm list --resource-group azcli-script-insight --query \"[].name\" -o tsv); do \n az vm show --resource-group azcli-script-insight --name $vm \n done" |
| 118 | + # 1. Translate CLI to Bicep |
| 119 | + print("=== CLI to Bicep Translation ===") |
| 120 | + translation_result = translate_cli_to_bicep(FUNCTION_APP_URL, sample_script) |
| 121 | + if translation_result.get("success"): |
| 122 | + print("Translation successful!") |
| 123 | + print(f"Bicep Template:\n{translation_result['bicep_template']}") |
| 124 | + else: |
| 125 | + print(f"Translation failed: {translation_result.get('error')}") |
| 126 | + |
| 127 | + # 2. What-If Preview (requires client-side Azure CLI credentials) |
| 128 | + print("\n=== What-If Preview (Client-side Azure CLI Auth) ===") |
| 129 | + whatif_cli_result = what_if_preview(FUNCTION_APP_URL, sample_script, subscription_id = '6b085460-5f21-477e-ba44-1035046e9101') |
| 130 | + if whatif_cli_result.get("success"): |
| 131 | + print("What-if preview with CLI auth successful!") |
| 132 | + print(f"Changes: {json.dumps(whatif_cli_result['what_if_result'], indent=2)}") |
| 133 | + else: |
| 134 | + print(f"{whatif_cli_result}") |
| 135 | + |
| 136 | + # 3. Analyze Script |
| 137 | + print("\n=== Script Analysis ===") |
| 138 | + analysis_result = analyze_azcli_script(FUNCTION_APP_URL, sample_script) |
| 139 | + if analysis_result.get("status") == "success": |
| 140 | + print("Analysis successful!") |
| 141 | + print(f"Analysis: {json.dumps(analysis_result['analysis'], indent=2)}") |
| 142 | + else: |
| 143 | + print(f"Analysis failed: {analysis_result.get('error')}") |
0 commit comments