diff --git a/cli/azd/extensions/azure.foundry/extension.yaml b/cli/azd/extensions/azure.foundry/extension.yaml new file mode 100644 index 00000000000..002aed623f7 --- /dev/null +++ b/cli/azd/extensions/azure.foundry/extension.yaml @@ -0,0 +1,15 @@ +# yaml-language-server: $schema=../extension.schema.json +id: azure.foundry +displayName: Azure Foundry Extension (Preview) +description: >- + Installs all Azure AI Foundry extensions for azd, including agents, + custom models, and fine-tuning. +version: 0.1.0-preview +capabilities: [] +dependencies: + - id: azure.ai.agents + version: latest + - id: azure.ai.models + version: latest + - id: azure.ai.finetune + version: latest diff --git a/cli/azd/extensions/registry.json b/cli/azd/extensions/registry.json index dfbd30f9359..4f79c7a1941 100644 --- a/cli/azd/extensions/registry.json +++ b/cli/azd/extensions/registry.json @@ -4274,6 +4274,38 @@ } } ] + }, + { + "id": "azure.foundry", + "displayName": "Azure Foundry Extension (Preview)", + "description": "Installs all Azure AI Foundry extensions for azd, including agents, custom models, and fine-tuning.", + "versions": [ + { + "version": "0.1.0-preview", + "usage": "azd extension install azure.foundry", + "examples": [ + { + "name": "install", + "description": "Install all Foundry extensions at once.", + "usage": "azd extension install azure.foundry" + } + ], + "dependencies": [ + { + "id": "azure.ai.agents", + "version": "latest" + }, + { + "id": "azure.ai.models", + "version": "latest" + }, + { + "id": "azure.ai.finetune", + "version": "latest" + } + ] + } + ] } ] -} \ No newline at end of file +} diff --git a/cli/installer/install-azd.ps1 b/cli/installer/install-azd.ps1 index 15b08232837..bc25aa9b87f 100644 --- a/cli/installer/install-azd.ps1 +++ b/cli/installer/install-azd.ps1 @@ -33,6 +33,10 @@ Skips verification of the downloaded file. .PARAMETER InstallShScriptUrl (Mac/Linux only) URL to the install-azd.sh script. Default is https://aka.ms/install-azd.sh +.PARAMETER InstallExtensions +Comma-separated list of azd extensions to install after azd is set up. +For example: --InstallExtensions "azure.foundry" or "azure.ai.agents,azure.ai.models" + .EXAMPLE powershell -ex AllSigned -c "Invoke-RestMethod 'https://aka.ms/install-azd.ps1' | Invoke-Expression" @@ -67,7 +71,8 @@ param( [switch] $SkipVerify, [int] $DownloadTimeoutSeconds = 120, [switch] $NoTelemetry, - [string] $InstallShScriptUrl = "https://aka.ms/install-azd.sh" + [string] $InstallShScriptUrl = "https://aka.ms/install-azd.sh", + [string] $InstallExtensions = "" ) function isLinuxOrMac { @@ -297,7 +302,47 @@ if (isLinuxOrMac) { $bashParameters = $params -join ' ' Write-Verbose "Running: curl -fsSL $InstallShScriptUrl | bash -s -- $bashParameters" -Verbose:$Verbose bash -c "curl -fsSL $InstallShScriptUrl | bash -s -- $bashParameters" - exit $LASTEXITCODE + + if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE + } + + # Handle extension install on Linux/Mac after bash script completes + if ($InstallExtensions) { + Write-Host "" + Write-Host "Installing requested extensions..." + + $azdCmd = Get-Command azd -ErrorAction SilentlyContinue + if ($azdCmd) { + $azdBin = $azdCmd.Source + } else { + Write-Warning "Could not locate azd after install. Extensions were not installed." + Write-Warning "Open a new terminal and run: azd extension install " + exit 0 + } + + $installExtFailed = $false + $extensions = $InstallExtensions -split ',' + foreach ($extId in $extensions) { + $extId = $extId.Trim() + if ($extId) { + Write-Host "Installing extension: $extId" + & $azdBin extension install $extId + if ($LASTEXITCODE -ne 0) { + Write-Warning "Failed to install extension: $extId. You can retry later with: azd extension install $extId" + $installExtFailed = $true + } else { + Write-Host "Extension $extId installed successfully" + } + } + } + + if ($installExtFailed) { + exit 1 + } + } + + exit 0 } try { @@ -388,6 +433,58 @@ try { Write-Host "" Write-Host "Read more about Azure Developer CLI telemetry: https://github.com/Azure/azure-dev#data-collection" + # Install extensions if requested (Windows path) + if ($InstallExtensions) { + Write-Host "" + Write-Host "Installing requested extensions..." + + # Resolve azd binary path + # If user specified InstallFolder, use that directly. + # Otherwise, the MSI updates PATH so use Get-Command to find it. + if ($InstallFolder) { + $azdBin = Join-Path $InstallFolder "azd.exe" + } else { + # Refresh PATH from registry since MSI updated it + $machinePath = [Environment]::GetEnvironmentVariable("Path", "Machine") + $userPath = [Environment]::GetEnvironmentVariable("Path", "User") + $env:Path = "$machinePath;$userPath" + + $azdCmd = Get-Command azd -ErrorAction SilentlyContinue + if ($azdCmd) { + $azdBin = $azdCmd.Source + } else { + Write-Warning "Could not locate azd after install. Extensions were not installed." + Write-Warning "Open a new terminal and run: azd extension install " + exit 0 + } + } + + $installExtFailed = $false + $extensions = $InstallExtensions -split ',' + foreach ($extId in $extensions) { + $extId = $extId.Trim() + if ($extId) { + Write-Host "Installing extension: $extId" + try { + & $azdBin extension install $extId + if ($LASTEXITCODE -ne 0) { + Write-Warning "Failed to install extension: $extId. You can retry later with: azd extension install $extId" + $installExtFailed = $true + } else { + Write-Host "Extension $extId installed successfully" + } + } catch { + Write-Warning "Failed to install extension: $extId. You can retry later with: azd extension install $extId" + $installExtFailed = $true + } + } + } + + if ($installExtFailed) { + exit 1 + } + } + exit 0 } catch { Write-Error -ErrorRecord $_ diff --git a/cli/installer/install-azd.sh b/cli/installer/install-azd.sh index bc8c0d18da7..ae92059d5d0 100755 --- a/cli/installer/install-azd.sh +++ b/cli/installer/install-azd.sh @@ -150,6 +150,7 @@ symlink_folder="/usr/local/bin" install_folder="/opt/microsoft/azd" no_telemetry=0 verbose=false +install_extensions="" while [[ $# -ne 0 ]]; do @@ -196,6 +197,14 @@ do -v|--verbose) verbose=true ;; + --install-extensions) + shift + if [[ $# -eq 0 ]]; then + say_error "--install-extensions requires a comma-separated list of extension IDs" + exit 1 + fi + install_extensions="$1" + ;; -h|-?|--help) script_name="$(basename "$0")" echo "Azure Dev CLI Installer" @@ -237,6 +246,10 @@ do echo " be sent and no prompt will be issued." echo "" echo " --verbose Enable verbose logging" + echo "" + echo " --install-extensions Comma-separated list of azd extensions to" + echo " install after azd is set up. e.g." + echo " --install-extensions azure.foundry" exit 0 ;; *) @@ -353,3 +366,31 @@ say "The Azure Developer CLI collects usage data and sends that usage data to Mi say "You can opt-out of telemetry by setting the AZURE_DEV_COLLECT_TELEMETRY environment variable to 'no' in the shell you use." say "" say "Read more about Azure Developer CLI telemetry: https://github.com/Azure/azure-dev#data-collection" + +# Install extensions if requested +if [ -n "$install_extensions" ]; then + azd_bin="$install_folder/$bin_name" + say "" + say "Installing requested extensions..." + + install_ext_failed=0 + IFS=',' read -ra ext_list <<< "$install_extensions" + for ext_id in "${ext_list[@]}"; do + ext_id="${ext_id#"${ext_id%%[![:space:]]*}"}" + ext_id="${ext_id%"${ext_id##*[![:space:]]}"}" + if [ -n "$ext_id" ]; then + say "Installing extension: $ext_id" + if ! "$azd_bin" extension install "$ext_id"; then + say_error "Failed to install extension: $ext_id" + say_error "You can retry later with: azd extension install $ext_id" + install_ext_failed=1 + else + say "Extension $ext_id installed successfully" + fi + fi + done + + if [ "$install_ext_failed" = 1 ]; then + exit 1 + fi +fi