Skip to content

Commit 9b379f9

Browse files
wbrezaCopilot
andcommitted
feat: add Script Provisioning Provider extension (microsoft.azd.scripts)
Implements the Script Provisioning Provider extension that enables shell script-based provisioning and teardown workflows in azd. This extension registers a 'scripts' provisioning provider via gRPC, allowing users to configure bash/PowerShell scripts as their infrastructure provider in azure.yaml. Key components: - Extension scaffold following microsoft.azd.demo pattern - Config parsing with validation (path safety, kind inference, platform overrides) - EnvResolver with 4-layer environment variable merging - ScriptExecutor for bash/pwsh script execution - OutputCollector for outputs.json discovery and parsing - Full ProvisioningProvider interface implementation Resolves #7734, #7735, #7736 Part of #7733 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent a2bef0b commit 9b379f9

File tree

22 files changed

+1595
-0
lines changed

22 files changed

+1595
-0
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# If you prefer the allow list template instead of the deny list, see community template:
2+
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
3+
#
4+
# Binaries for programs and plugins
5+
*.exe
6+
*.exe~
7+
*.dll
8+
*.so
9+
*.dylib
10+
11+
# Test binary, built with `go test -c`
12+
*.test
13+
14+
# Output of the go coverage tool, specifically when used with LiteIDE
15+
*.out
16+
17+
# Dependency directories (remove the comment below to include it)
18+
# vendor/
19+
20+
# Go workspace file
21+
go.work
22+
go.work.sum
23+
24+
# env file
25+
.env
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Release History
2+
3+
## 0.1.0-alpha.1 (Unreleased)
4+
5+
### Features Added
6+
7+
- Initial scaffold for the Script Provisioning Provider extension.
8+
- Registers `scripts` provisioning provider with azd via gRPC.
9+
- Configuration parsing for `provision` and `destroy` script lists from `azure.yaml`.
10+
- Script execution with 4-layer environment variable merging.
11+
- Output collection from `outputs.json` files.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Script Provisioning Provider (`microsoft.azd.scripts`)
2+
3+
A first-party azd extension that enables shell script-based provisioning and teardown workflows.
4+
5+
## Overview
6+
7+
The Script Provisioning Provider allows teams with existing script-based infrastructure workflows to adopt azd without rewriting their provisioning logic. Configure bash or PowerShell scripts as your provisioning provider in `azure.yaml`, and azd will execute them during `azd provision` and `azd down`.
8+
9+
## Installation
10+
11+
```bash
12+
azd ext install microsoft.azd.scripts
13+
```
14+
15+
## Configuration
16+
17+
Configure the extension in your `azure.yaml`:
18+
19+
```yaml
20+
infra:
21+
provider: scripts
22+
config:
23+
provision:
24+
- kind: sh
25+
run: scripts/setup.sh
26+
name: Setup Infrastructure
27+
env:
28+
AZURE_LOCATION: ${AZURE_LOCATION}
29+
RESOURCE_GROUP: rg-${AZURE_ENV_NAME}
30+
- kind: pwsh
31+
run: scripts/configure-app.ps1
32+
env:
33+
APP_NAME: myapp-${AZURE_ENV_NAME}
34+
destroy:
35+
- kind: sh
36+
run: scripts/teardown.sh
37+
env:
38+
RESOURCE_GROUP: rg-${AZURE_ENV_NAME}
39+
```
40+
41+
### Script Configuration Options
42+
43+
| Field | Type | Required | Description |
44+
|-------|------|----------|-------------|
45+
| `run` | string | Yes | Relative path to the script file |
46+
| `kind` | string | No | Script type: `sh` or `pwsh` (auto-detected from extension) |
47+
| `name` | string | No | Display name for progress reporting |
48+
| `env` | map | No | Environment variables with `${EXPRESSION}` substitution |
49+
| `secrets` | map | No | Secret references (`akvs://vault/secret`) |
50+
| `continueOnError` | bool | No | Continue execution on script failure |
51+
| `windows` | object | No | Windows-specific overrides |
52+
| `posix` | object | No | Linux/macOS-specific overrides |
53+
54+
### Environment Variable Resolution
55+
56+
Variables are resolved using a 4-layer priority model:
57+
58+
1. **OS environment** (lowest priority)
59+
2. **azd environment** values (`.env` + prior script outputs)
60+
3. **`env` map** values (`${EXPRESSION}` substitution)
61+
4. **`secrets` map** values (highest priority)
62+
63+
### Output Collection
64+
65+
Scripts can produce outputs by writing an `outputs.json` file alongside the script:
66+
67+
```json
68+
{
69+
"WEBSITE_URL": { "type": "string", "value": "https://myapp.azurewebsites.net" },
70+
"RESOURCE_GROUP": { "type": "string", "value": "rg-myapp-dev" }
71+
}
72+
```
73+
74+
Outputs are merged across scripts and stored in the azd environment.
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# Ensure script fails on any error
2+
$ErrorActionPreference = 'Stop'
3+
4+
# Get the directory of the script
5+
$EXTENSION_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path
6+
7+
# Change to the script directory
8+
Set-Location -Path $EXTENSION_DIR
9+
10+
# Create a safe version of EXTENSION_ID replacing dots with dashes
11+
$EXTENSION_ID_SAFE = $env:EXTENSION_ID -replace '\.', '-'
12+
13+
# Define output directory
14+
$OUTPUT_DIR = if ($env:OUTPUT_DIR) { $env:OUTPUT_DIR } else { Join-Path $EXTENSION_DIR "bin" }
15+
16+
# Create output directory if it doesn't exist
17+
if (-not (Test-Path -Path $OUTPUT_DIR)) {
18+
New-Item -ItemType Directory -Path $OUTPUT_DIR | Out-Null
19+
}
20+
21+
# Get Git commit hash and build date
22+
$COMMIT = git rev-parse HEAD
23+
if ($LASTEXITCODE -ne 0) {
24+
Write-Host "Error: Failed to get git commit hash"
25+
exit 1
26+
}
27+
$BUILD_DATE = (Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ")
28+
29+
# List of OS and architecture combinations
30+
if ($env:EXTENSION_PLATFORM) {
31+
$PLATFORMS = @($env:EXTENSION_PLATFORM)
32+
} else {
33+
$PLATFORMS = @(
34+
"windows/amd64",
35+
"windows/arm64",
36+
"darwin/amd64",
37+
"darwin/arm64",
38+
"linux/amd64",
39+
"linux/arm64"
40+
)
41+
}
42+
43+
$APP_PATH = "github.com/azure/azure-dev/cli/azd/extensions/$env:EXTENSION_ID/internal/cmd"
44+
45+
# Check if the build type is specified
46+
if (-not $env:EXTENSION_LANGUAGE) {
47+
Write-Host "Error: BUILD_TYPE environment variable is required (go or dotnet)"
48+
exit 1
49+
}
50+
51+
# Loop through platforms and build
52+
foreach ($PLATFORM in $PLATFORMS) {
53+
$OS, $ARCH = $PLATFORM -split '/'
54+
55+
$OUTPUT_NAME = Join-Path $OUTPUT_DIR "$EXTENSION_ID_SAFE-$OS-$ARCH"
56+
57+
if ($OS -eq "windows") {
58+
$OUTPUT_NAME += ".exe"
59+
}
60+
61+
Write-Host "Building for $OS/$ARCH..."
62+
63+
# Delete the output file if it already exists
64+
if (Test-Path -Path $OUTPUT_NAME) {
65+
Remove-Item -Path $OUTPUT_NAME -Force
66+
}
67+
68+
if ($env:EXTENSION_LANGUAGE -eq "dotnet") {
69+
# Set runtime identifier for .NET
70+
$RUNTIME = if ($OS -eq "windows") { "win-$ARCH" } elseif ($OS -eq "darwin") { "osx-$ARCH" } else { "linux-$ARCH" }
71+
$PROJECT_FILE = "$EXTENSION_ID_SAFE.csproj"
72+
73+
# Run dotnet publish for single file executable
74+
dotnet publish `
75+
-c Release `
76+
-r $RUNTIME `
77+
-o $OUTPUT_DIR `
78+
/p:PublishTrimmed=true `
79+
$PROJECT_FILE
80+
81+
if ($LASTEXITCODE -ne 0) {
82+
Write-Host "An error occurred while building for $OS/$ARCH"
83+
exit 1
84+
}
85+
86+
$EXPECTED_OUTPUT_NAME = $EXTENSION_ID_SAFE
87+
if ($OS -eq "windows") {
88+
$EXPECTED_OUTPUT_NAME += ".exe"
89+
}
90+
91+
Rename-Item -Path "$OUTPUT_DIR/$EXPECTED_OUTPUT_NAME" -NewName $OUTPUT_NAME
92+
} elseif ($env:EXTENSION_LANGUAGE -eq "go") {
93+
# Set environment variables for Go build
94+
$env:GOOS = $OS
95+
$env:GOARCH = $ARCH
96+
97+
go build `
98+
-ldflags="-X '$APP_PATH.Version=$env:EXTENSION_VERSION' -X '$APP_PATH.Commit=$COMMIT' -X '$APP_PATH.BuildDate=$BUILD_DATE'" `
99+
-o $OUTPUT_NAME
100+
101+
if ($LASTEXITCODE -ne 0) {
102+
Write-Host "An error occurred while building for $OS/$ARCH"
103+
exit 1
104+
}
105+
} else {
106+
Write-Host "Error: Unsupported BUILD_TYPE '$env:BUILD_TYPE'. Use 'go' or 'dotnet'."
107+
exit 1
108+
}
109+
}
110+
111+
Write-Host "Build completed successfully!"
112+
Write-Host "Binaries are located in the $OUTPUT_DIR directory."
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/bin/bash
2+
3+
# Get the directory of the script
4+
EXTENSION_DIR="$(cd "$(dirname "$0")" && pwd)"
5+
6+
# Change to the script directory
7+
cd "$EXTENSION_DIR" || exit
8+
9+
# Create a safe version of EXTENSION_ID replacing dots with dashes
10+
EXTENSION_ID_SAFE="${EXTENSION_ID//./-}"
11+
12+
# Define output directory
13+
OUTPUT_DIR="${OUTPUT_DIR:-$EXTENSION_DIR/bin}"
14+
15+
# Create output and target directories if they don't exist
16+
mkdir -p "$OUTPUT_DIR"
17+
18+
# Get Git commit hash and build date
19+
COMMIT=$(git rev-parse HEAD)
20+
BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)
21+
22+
# List of OS and architecture combinations
23+
if [ -n "$EXTENSION_PLATFORM" ]; then
24+
PLATFORMS=("$EXTENSION_PLATFORM")
25+
else
26+
PLATFORMS=(
27+
"windows/amd64"
28+
"windows/arm64"
29+
"darwin/amd64"
30+
"darwin/arm64"
31+
"linux/amd64"
32+
"linux/arm64"
33+
)
34+
fi
35+
36+
APP_PATH="$EXTENSION_ID/internal/cmd"
37+
38+
# Loop through platforms and build
39+
for PLATFORM in "${PLATFORMS[@]}"; do
40+
OS=$(echo "$PLATFORM" | cut -d'/' -f1)
41+
ARCH=$(echo "$PLATFORM" | cut -d'/' -f2)
42+
43+
OUTPUT_NAME="$OUTPUT_DIR/$EXTENSION_ID_SAFE-$OS-$ARCH"
44+
45+
if [ "$OS" = "windows" ]; then
46+
OUTPUT_NAME+='.exe'
47+
fi
48+
49+
echo "Building for $OS/$ARCH..."
50+
51+
# Delete the output file if it already exists
52+
[ -f "$OUTPUT_NAME" ] && rm -f "$OUTPUT_NAME"
53+
54+
# Set environment variables for Go build
55+
GOOS=$OS GOARCH=$ARCH go build \
56+
-ldflags="-X '$APP_PATH.Version=$EXTENSION_VERSION' -X '$APP_PATH.Commit=$COMMIT' -X '$APP_PATH.BuildDate=$BUILD_DATE'" \
57+
-o "$OUTPUT_NAME"
58+
59+
if [ $? -ne 0 ]; then
60+
echo "An error occurred while building for $OS/$ARCH"
61+
exit 1
62+
fi
63+
done
64+
65+
echo "Build completed successfully!"
66+
echo "Binaries are located in the $OUTPUT_DIR directory."

0 commit comments

Comments
 (0)