Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions cli/azd/extensions/azure.foundry/extension.yaml
Original file line number Diff line number Diff line change
@@ -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
34 changes: 33 additions & 1 deletion cli/azd/extensions/registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
]
}
]
}
]
}
}
101 changes: 99 additions & 2 deletions cli/installer/install-azd.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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 = ""
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
[string] $InstallExtensions = ""
[string[]] $InstallExtensions = []

The invocation might look like:

install-azd.ps1 -InstallExtensions azure.ai.agents,azure.ai.models

PowerShell automatically interprets items separated by commas (no spaces) as an array and [string[]] coerces to strings for later passing through to the script.

)

function isLinuxOrMac {
Expand Down Expand Up @@ -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
Comment on lines +311 to +317
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of calling azd after the bash script completes, map $InstallExtenions to the bash call through $bashParameters

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be done by joining $InstallExtensions with commas.. Something like

if ($InstallExtensions) { 
    $params += "--install-extensions ${$InstallExtensions -join ','}"
}

} else {
Write-Warning "Could not locate azd after install. Extensions were not installed."
Write-Warning "Open a new terminal and run: azd extension install <extension-id>"
exit 0
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When -InstallExtensions is provided but azd can't be found, this exits 0. CI/CD pipelines won't detect that the requested extensions weren't installed. Same pattern at line 458 (Windows path).

Extension install failure already returns 1 (line 341). This creates an inconsistency: "extension install failed" = exit 1, but "couldn't even try" = exit 0. Consider exit 1 here since the user explicitly asked for extensions.

}

$installExtFailed = $false
$extensions = $InstallExtensions -split ','
foreach ($extId in $extensions) {
$extId = $extId.Trim()
if ($extId) {
Write-Host "Installing extension: $extId"
& $azdBin extension install $extId
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Windows extension block wraps this in try/catch (line 468) but the Linux/Mac block doesn't. If $PSNativeCommandUseErrorActionPreference is enabled or the binary path becomes invalid between lookup and invocation, you'd get an unhandled error instead of the friendly warning. Minor consistency nit - the Windows code already has the right pattern.

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 {
Expand Down Expand Up @@ -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"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does mutate the current session's $env:PATH... in many instances this might not be a problem but it could violate expectations of someone who downloads the script to a file and runs it directly from the prompt.


$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 <extension-id>"
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 $_
Expand Down
41 changes: 41 additions & 0 deletions cli/installer/install-azd.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -237,6 +246,10 @@ do
echo " be sent and no prompt will be issued."
echo ""
echo " --verbose Enable verbose logging"
echo ""
echo " --install-extensions <LIST> Comma-separated list of azd extensions to"
echo " install after azd is set up. e.g."
echo " --install-extensions azure.foundry"
exit 0
;;
*)
Expand Down Expand Up @@ -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
Loading