Skip to content

AI Builder Extension - App Service support #5104

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 31 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2871c43
WIP: Start of AI builder extension
wbreza Mar 6, 2025
2442f56
Adds questions & resource selection prompts
wbreza Mar 7, 2025
40fbc9b
Adds support for state between questions
wbreza Mar 7, 2025
a9bd011
Adds help and fixes
wbreza Mar 7, 2025
eb9344e
Adds support for model selection
wbreza Mar 8, 2025
807fbc8
wip: model selection
wbreza Mar 12, 2025
2f248c9
minor updates
wbreza Mar 12, 2025
2abca9e
WIP: Adding resources to composability
wbreza Mar 13, 2025
9d98530
Adds workflow service and updates compose service
wbreza Mar 13, 2025
402d1ef
Fixes issues selected models that do not have default versions specified
wbreza Mar 14, 2025
ac6a560
Adds WIP files for scaffolded services
wbreza Mar 14, 2025
b5b5784
Adds copy check
wbreza Mar 14, 2025
d7265c5
Adds AI search vector store
wbreza Mar 14, 2025
0aedd2b
Updates code samples for extension
wbreza Mar 20, 2025
354a11f
Adds ability to reuse existing composed resources
wbreza Mar 20, 2025
d1d4822
Adds language defaults for app types
wbreza Mar 20, 2025
678603c
Supports appending models to AI project resource
wbreza Mar 21, 2025
ccfbf56
Fixes lint warnings
wbreza Mar 21, 2025
54b091d
Adds dependent resources uses blocks
wbreza Mar 21, 2025
b74f55e
Updates inline docs
wbreza Mar 21, 2025
26995a0
Fixes some minor issues related to uses and existing resources
wbreza Mar 24, 2025
d29369c
Fixes Agent flow
wbreza Mar 24, 2025
9e27b28
Fixes linter issues
wbreza Mar 24, 2025
8939a6d
Auto init project and environments when not available
wbreza Mar 27, 2025
ba95c5f
list resource type for extension (#4997)
hemarina Apr 15, 2025
c848999
Rebased main
wbreza Apr 16, 2025
731a1c2
Removed app files for now
wbreza Apr 16, 2025
11a03df
Fixes linter issues
wbreza Apr 17, 2025
e93b6fd
Addresses PR feedback
wbreza Apr 17, 2025
b8d60bf
Add App Service support in AI builder extension
JeffreyCA Apr 18, 2025
2e1f87c
Fix lint warning
JeffreyCA Apr 18, 2025
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
5 changes: 5 additions & 0 deletions cli/azd/.vscode/cspell.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,11 @@ overrides:
- getenv
- errorf
- println
- filename: extensions/microsoft.azd.ai.builder/internal/cmd/start.go
words:
- dall
- datasource
- vectorizing
ignorePaths:
- "**/*_test.go"
- "**/mock*.go"
7 changes: 5 additions & 2 deletions cli/azd/cmd/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,12 +197,15 @@ func registerCommonDependencies(container *ioc.NestedContainer) {
})

// Azd Context
container.MustRegisterSingleton(func(lazyAzdContext *lazy.Lazy[*azdcontext.AzdContext]) (*azdcontext.AzdContext, error) {
// Scoped registration is required since the value of the azd context can change through the lifetime of a command
// Example: Within extensions multiple workflows can be dispatched which can cause the azd context to be updated.
// A specific example is within AI builder. It invokes `init` command when project is not found.
container.MustRegisterScoped(func(lazyAzdContext *lazy.Lazy[*azdcontext.AzdContext]) (*azdcontext.AzdContext, error) {
return lazyAzdContext.GetValue()
})

// Lazy loads the Azd context after the azure.yaml file becomes available
container.MustRegisterSingleton(func() *lazy.Lazy[*azdcontext.AzdContext] {
container.MustRegisterScoped(func() *lazy.Lazy[*azdcontext.AzdContext] {
return lazy.NewLazy(azdcontext.NewAzdContext)
})

Expand Down
7 changes: 6 additions & 1 deletion cli/azd/docs/extension-framework.md
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,12 @@ if err := eventManager.Receive(ctx); err != nil {
- Generated files @ [pkg/azdext](../pkg/azdext)
- Make file @ [Makefile](../Makefile)

To re-generate gRPC clients run `make proto` from the `~/cli/azd` folder of the repo.
To re-generate gRPC clients:
- Run `protoc --version` to check if `protoc` is installed. If not, download and install it from [GitHub](https://github.com./protocolbuffers/protobuf/releases).
- Run `make --version` to check if `make` is installed.
- Run `go install google.golang.org/protobuf/cmd/protoc-gen-go@latest` and `go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest` to install the required Go tools.
- Run `make proto` from the `~/cli/azd` folder of the repo in `Git Bash`.
- Run `../../eng/scripts/copyright-check.sh . --fix` to add copyright.

## gRPC Services

Expand Down
25 changes: 25 additions & 0 deletions cli/azd/extensions/microsoft.azd.ai.builder/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com./github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

# Go workspace file
go.work
go.work.sum

# env file
.env
19 changes: 19 additions & 0 deletions cli/azd/extensions/microsoft.azd.ai.builder/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# `azd` AI Builder Extension

The AI Builder extension for the Azure Developer CLI (azd) is your intelligent assistant for integrating AI into your applications.
Designed to simplify the journey of adding AI capabilities, this extension offers a guided, scenario-driven experience that helps you discover the right set of Azure resources based on your use case.

Whether you're building Retrieval-Augmented Generation (RAG) systems, intelligent agents, or custom AI workflows, AI Builder walks you through best-practice architectures and provisioning steps tailored for each scenario.

It supports identifying and provisioning common AI-related Azure resources such as:

- Azure OpenAI Service – for foundational models, chat completion, and embedding APIs
- Azure AI Search (Cognitive Search) – to build powerful, indexed knowledge retrieval layers
- Azure Blob Storage – for storing unstructured data like documents, PDFs, and datasets
- Azure Cosmos DB – for storing structured conversation history or knowledge graphs
- Azure App Service / Azure Container Apps – to host APIs, agents, or front-end integrations
- Azure Functions – for lightweight orchestration or retrieval logic
- Azure Key Vault – to securely manage API keys and secrets
- Azure AI Document Intelligence (Coming soon) – for processing semi-structured or scanned documents

By combining LLM-aware prompts with best-practice guidance, the AI Builder ensures your app architecture is aligned with your AI goals—from POCs to production.
105 changes: 105 additions & 0 deletions cli/azd/extensions/microsoft.azd.ai.builder/build.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Get the directory of the script
$EXTENSION_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path

# Change to the script directory
Set-Location -Path $EXTENSION_DIR

# Create a safe version of EXTENSION_ID replacing dots with dashes
$EXTENSION_ID_SAFE = $env:EXTENSION_ID -replace '\.', '-'

# Define output directory
$OUTPUT_DIR = if ($env:OUTPUT_DIR) { $env:OUTPUT_DIR } else { Join-Path $EXTENSION_DIR "bin" }

# Create output directory if it doesn't exist
if (-not (Test-Path -Path $OUTPUT_DIR)) {
New-Item -ItemType Directory -Path $OUTPUT_DIR | Out-Null
}

# Get Git commit hash and build date
$COMMIT = git rev-parse HEAD
$BUILD_DATE = (Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ")

# List of OS and architecture combinations
if ($env:EXTENSION_PLATFORM) {
$PLATFORMS = @($env:EXTENSION_PLATFORM)
} else {
$PLATFORMS = @(
"windows/amd64",
"windows/arm64",
"darwin/amd64",
"darwin/arm64",
"linux/amd64",
"linux/arm64"
)
}

$APP_PATH = "github.com./azure/azure-dev/cli/azd/extensions/$env:EXTENSION_ID/internal/cmd"

# Check if the build type is specified
if (-not $env:EXTENSION_LANGUAGE) {
Write-Host "Error: BUILD_TYPE environment variable is required (go or dotnet)"
exit 1
}

# Loop through platforms and build
foreach ($PLATFORM in $PLATFORMS) {
$OS, $ARCH = $PLATFORM -split '/'

$OUTPUT_NAME = Join-Path $OUTPUT_DIR "$EXTENSION_ID_SAFE-$OS-$ARCH"

if ($OS -eq "windows") {
$OUTPUT_NAME += ".exe"
}

Write-Host "Building for $OS/$ARCH..."

# Delete the output file if it already exists
if (Test-Path -Path $OUTPUT_NAME) {
Remove-Item -Path $OUTPUT_NAME -Force
}

if ($env:EXTENSION_LANGUAGE -eq "dotnet") {
# Set runtime identifier for .NET
$RUNTIME = if ($OS -eq "windows") { "win-$ARCH" } elseif ($OS -eq "darwin") { "osx-$ARCH" } else { "linux-$ARCH" }
$PROJECT_FILE = "azd-extension.csproj"

# Run dotnet publish for single file executable
dotnet publish `
-c Release `
-r $RUNTIME `
-o $OUTPUT_DIR `
/p:PublishTrimmed=true `
$PROJECT_FILE

if ($LASTEXITCODE -ne 0) {
Write-Host "An error occurred while building for $OS/$ARCH"
exit 1
}

$EXPECTED_OUTPUT_NAME = $EXTENSION_ID_SAFE
if ($OS -eq "windows") {
$EXPECTED_OUTPUT_NAME += ".exe"
}

Rename-Item -Path "$OUTPUT_DIR/$EXPECTED_OUTPUT_NAME" -NewName $OUTPUT_NAME
} elseif ($env:EXTENSION_LANGUAGE -eq "go") {
# Set environment variables for Go build
$env:GOOS = $OS
$env:GOARCH = $ARCH

go build `
-ldflags="-X '$APP_PATH.Version=$env:EXTENSION_VERSION' -X '$APP_PATH.Commit=$COMMIT' -X '$APP_PATH.BuildDate=$BUILD_DATE'" `
-o $OUTPUT_NAME

if ($LASTEXITCODE -ne 0) {
Write-Host "An error occurred while building for $OS/$ARCH"
exit 1
}
} else {
Write-Host "Error: Unsupported BUILD_TYPE '$env:BUILD_TYPE'. Use 'go' or 'dotnet'."
exit 1
}
}

Write-Host "Build completed successfully!"
Write-Host "Binaries are located in the $OUTPUT_DIR directory."
107 changes: 107 additions & 0 deletions cli/azd/extensions/microsoft.azd.ai.builder/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#!/bin/bash

# Get the directory of the script
EXTENSION_DIR="$(cd "$(dirname "$0")" && pwd)"

# Change to the script directory
cd "$EXTENSION_DIR" || exit

# Create a safe version of EXTENSION_ID replacing dots with dashes
EXTENSION_ID_SAFE="${EXTENSION_ID//./-}"

# Define output directory
OUTPUT_DIR="${OUTPUT_DIR:-$EXTENSION_DIR/bin}"

# Create output and target directories if they don't exist
mkdir -p "$OUTPUT_DIR"

# Get Git commit hash and build date
COMMIT=$(git rev-parse HEAD)
BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)

# List of OS and architecture combinations
if [ -n "$EXTENSION_PLATFORM" ]; then
PLATFORMS=("$EXTENSION_PLATFORM")
else
PLATFORMS=(
"windows/amd64"
"windows/arm64"
"darwin/amd64"
"darwin/arm64"
"linux/amd64"
"linux/arm64"
)
fi

APP_PATH="github.com./azure/azure-dev/cli/azd/extensions/$EXTENSION_ID/internal/cmd"

# Check if the build type is specified
if [ -z "$EXTENSION_LANGUAGE" ]; then
echo "Error: EXTENSION_LANGUAGE environment variable is required (go or dotnet)"
exit 1
fi

# Loop through platforms and build
for PLATFORM in "${PLATFORMS[@]}"; do
OS=$(echo "$PLATFORM" | cut -d'/' -f1)
ARCH=$(echo "$PLATFORM" | cut -d'/' -f2)

OUTPUT_NAME="$OUTPUT_DIR/$EXTENSION_ID_SAFE-$OS-$ARCH"

if [ "$OS" = "windows" ]; then
OUTPUT_NAME+='.exe'
fi

echo "Building for $OS/$ARCH..."

# Delete the output file if it already exists
[ -f "$OUTPUT_NAME" ] && rm -f "$OUTPUT_NAME"

if [ "$EXTENSION_LANGUAGE" = "dotnet" ]; then
# Set runtime identifier for .NET
if [ "$OS" = "windows" ]; then
RUNTIME="win-$ARCH"
elif [ "$OS" = "darwin" ]; then
RUNTIME="osx-$ARCH"
else
RUNTIME="linux-$ARCH"
fi
PROJECT_FILE="azd-extension.csproj"

# Run dotnet publish for single file executable
dotnet publish \
-c Release \
-r "$RUNTIME" \
-o "$OUTPUT_DIR" \
/p:PublishTrimmed=true \
"$PROJECT_FILE"

if [ $? -ne 0 ]; then
echo "An error occurred while building for $OS/$ARCH"
exit 1
fi

EXPECTED_OUTPUT_NAME="$EXTENSION_ID_SAFE"
if [ "$OS" = "windows" ]; then
EXPECTED_OUTPUT_NAME+='.exe'
fi

mv "$OUTPUT_DIR/$EXPECTED_OUTPUT_NAME" "$OUTPUT_NAME"
elif [ "$EXTENSION_LANGUAGE" = "go" ]; then
# Set environment variables for Go build
GOOS=$OS GOARCH=$ARCH go build \
-ldflags="-X '$APP_PATH.Version=$EXTENSION_VERSION' -X '$APP_PATH.Commit=$COMMIT' -X '$APP_PATH.BuildDate=$BUILD_DATE'" \
-o "$OUTPUT_NAME"

if [ $? -ne 0 ]; then
echo "An error occurred while building for $OS/$ARCH"
exit 1
fi
else
echo "Error: Unsupported EXTENSION_LANGUAGE '$EXTENSION_LANGUAGE'. Use 'go' or 'dotnet'."
exit 1
fi
done

echo "Build completed successfully!"
echo "Binaries are located in the $OUTPUT_DIR directory."
14 changes: 14 additions & 0 deletions cli/azd/extensions/microsoft.azd.ai.builder/extension.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# yaml-language-server: $schema=../extension.schema.json
id: microsoft.azd.ai.builder
namespace: ai
displayName: AZD AI Builder
description: This extension provides custom commands for building AI applications using Azure Developer CLI.
usage: azd ai builder <command> [options]
version: 0.1.0
language: go
capabilities:
- custom-commands
examples:
- name: start
description: Provides a guided experience for building AI applications using Azure Developer CLI.
usage: azd ai builder start
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package cmd

import (
"github.com./spf13/cobra"
)

func NewRootCommand() *cobra.Command {
rootCmd := &cobra.Command{
Use: "azd ai builder <command> [options]",
Short: "Runs the Azure AI Builder.",
SilenceUsage: true,
SilenceErrors: true,
CompletionOptions: cobra.CompletionOptions{
DisableDefaultCmd: true,
},
}

rootCmd.SetHelpCommand(&cobra.Command{Hidden: true})
rootCmd.PersistentFlags().Bool("debug", false, "Enable debug mode")

rootCmd.AddCommand(newStartCommand())
rootCmd.AddCommand(newVersionCommand())

return rootCmd
}
Loading