Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ defaults:
working-directory: cli

env:
GO_VERSION: '1.25.7'
GO_VERSION: '1.26.0'

jobs:
preflight:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ defaults:
working-directory: cli

env:
GO_VERSION: '1.25.7'
GO_VERSION: '1.26.0'

jobs:
# Check if build should run
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ defaults:
working-directory: cli

env:
GO_VERSION: '1.25.7'
GO_VERSION: '1.26.0'

jobs:
release:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ defaults:
working-directory: cli

env:
GO_VERSION: '1.25.7'
GO_VERSION: '1.26.0'

jobs:
preflight:
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ We use GitHub to host code, to track issues and feature requests, as well as acc

### Prerequisites

- Go 1.25.5 or later
- Go 1.26.0 or later
- golangci-lint
- Node.js 20+ (for cspell)
- Git
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ Binary created in `cli/bin/exec`.

### Prerequisites

- Go 1.25.5 or later
- Go 1.26.0 or later
- golangci-lint
- Node.js 20+ (for cspell)

Expand Down
2 changes: 1 addition & 1 deletion TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ pnpm exec playwright test --project=chromium
## Prerequisites

### CLI Tests
- Go 1.25.5 or later
- Go 1.26.0 or later
- Optional: `golangci-lint` for linting

### Web Tests
Expand Down
4 changes: 2 additions & 2 deletions cli/go.mod
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
module github.com/jongio/azd-exec/cli

go 1.25.7
go 1.26.0

require (
github.com/jongio/azd-core v0.4.1
github.com/jongio/azd-core v0.4.4
github.com/magefile/mage v1.15.0
github.com/spf13/cobra v1.10.2
)
Expand Down
4 changes: 2 additions & 2 deletions cli/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jongio/azd-core v0.4.1 h1:W3HlPKUxroqKIvBR5yhb/lw1cngDm5G9oV+3x9e+Pck=
github.com/jongio/azd-core v0.4.1/go.mod h1:83pqhWjImIe1aeos7duHnU581wy//yzUs29YdOysM80=
github.com/jongio/azd-core v0.4.4 h1:E2dxfxkiqyf3c7WNLbehgimZMiVHxks7X4zT6kr5iiE=
github.com/jongio/azd-core v0.4.4/go.mod h1:lqGHertCK/AFcJHuPHFz+gpkEBx4Oj9+NoQIWdINqVo=
github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
Expand Down
8 changes: 8 additions & 0 deletions cli/src/cmd/exec/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/jongio/azd-core/env"
"github.com/jongio/azd-exec/cli/src/cmd/exec/commands"
"github.com/jongio/azd-exec/cli/src/internal/executor"
"github.com/jongio/azd-exec/cli/src/internal/skills"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -142,6 +143,13 @@ Examples:
_ = os.Setenv("AZD_TRACE_LOG_URL", traceLogURL)
}

// Install Copilot skill
if err := skills.InstallSkill(); err != nil {
if debugMode {
fmt.Fprintf(os.Stderr, "Warning: failed to install copilot skill: %v\n", err)
}
}

return nil
},
}
Expand Down
159 changes: 159 additions & 0 deletions cli/src/internal/skills/azd-exec/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
---
name: azd-exec
description: |
Execute scripts and commands with Azure Developer CLI context, environment variables,
and automatic Azure Key Vault secret resolution. USE FOR: run scripts with azure context,
execute commands with env vars, resolve key vault secrets, cross-platform scripting,
interactive script execution, shell selection. DO NOT USE FOR: service orchestration
(use azd-app), Azure deployments (use azd deploy), long-running services.
---

# azd-exec Extension

azd-exec is an Azure Developer CLI extension that executes commands and scripts
with full access to azd environment variables, Azure credentials, and automatic
Azure Key Vault secret resolution.

## When to Use

- Running scripts that need azd environment variables (AZURE_ENV_NAME, AZURE_SUBSCRIPTION_ID, etc.)
- Executing commands that reference Azure Key Vault secrets in environment variables
- Cross-platform scripting with automatic shell detection
- Interactive script execution requiring stdin passthrough

## Command Syntax

```
azd exec [flags] <script-or-command> [-- script-args...]
```

The first positional argument is either a **file path** (executed as a script) or an
**inline command string** (executed in the detected/specified shell). Everything after
the script argument (or after `--`) is passed as arguments to the script.

## Flags

| Flag | Short | Default | Description |
|------|-------|---------|-------------|
| `--shell` | `-s` | auto | Shell to use: `bash`, `sh`, `zsh`, `pwsh`, `powershell`, `cmd` |
| `--interactive` | `-i` | false | Connect stdin to the script for interactive input |
| `--stop-on-keyvault-error` | | false | Fail-fast when any Key Vault reference fails to resolve |
| `--cwd` | `-C` | . | Set working directory before execution |
| `--environment` | `-e` | | Load a specific azd environment by name |
| `--debug` | | false | Enable debug output to stderr |
| `--output` | `-o` | default | Output format: `default` or `json` |

## Shell Support

### Supported Shells

`bash`, `sh`, `zsh`, `pwsh` (PowerShell Core 6+), `powershell` (Windows PowerShell 5.1), `cmd`

### Auto-Detection Logic

When `--shell` is not specified:

1. **File scripts**: shell is detected from the file extension (`.sh` → bash, `.ps1` → pwsh,
`.cmd`/`.bat` → cmd) or shebang line (`#!/bin/bash`, etc.)
2. **Inline commands**: defaults to `bash` on Linux/macOS, `powershell` on Windows

## Key Vault Secret Resolution

azd-exec automatically resolves Azure Key Vault references found in environment variables
before executing the script. Three reference formats are supported:

### Format 1: SecretUri

```
@Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/my-secret)
@Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/my-secret/version-id)
```

### Format 2: VaultName + SecretName

```
@Microsoft.KeyVault(VaultName=myvault;SecretName=my-secret)
@Microsoft.KeyVault(VaultName=myvault;SecretName=my-secret;SecretVersion=version-id)
```

### Format 3: akvs:// URI

```
akvs://subscription-id/myvault/my-secret
akvs://subscription-id/myvault/my-secret/version-id
```

### Resolution Behavior

- By default, unresolvable references log a warning and the original value is kept.
- With `--stop-on-keyvault-error`, any resolution failure aborts execution immediately.
- Resolution uses the current Azure credential (e.g., `az login` session or managed identity).

## Argument Passing

Everything after the script path is forwarded as script arguments. The `--` separator
is optional but useful to prevent flag conflicts:

```bash
azd exec ./build.sh --verbose # --verbose passed to build.sh
azd exec ./build.sh -- --verbose # same, explicit separator
azd exec ./deploy.sh -- --env staging # avoids conflict with azd's -e flag
```

## Interactive Mode

Use `--interactive` / `-i` to connect stdin to the script process. This is required
for scripts that prompt for user input:

```bash
azd exec -i ./setup-wizard.sh
```

Without `-i`, stdin is not connected and the script cannot read interactive input.

## Error Handling

azd-exec uses typed errors:

| Error | Cause |
|-------|-------|
| `ValidationError` | Invalid input (empty path, path traversal, empty inline script) |
| `ScriptNotFoundError` | Script file does not exist |
| `InvalidShellError` | Unknown shell name passed to `--shell` |
| `ExecutionError` | Script exited with a non-zero exit code |

The process exit code from the script is propagated to the caller.

## Examples

```bash
# Execute a script file with azd environment loaded
azd exec ./setup.sh

# Run inline bash command with environment variables
azd exec 'echo $AZURE_ENV_NAME'

# Use PowerShell explicitly
azd exec --shell pwsh "Write-Host 'Hello from PowerShell'"

# Execute a PowerShell script file
azd exec --shell pwsh ./deploy.ps1

# Pass arguments to a script
azd exec ./build.sh -- --target release --verbose

# Interactive mode for scripts that prompt for input
azd exec -i ./interactive-setup.sh

# Use a specific azd environment
azd exec -e staging ./migrate-db.sh

# Fail-fast on Key Vault errors
azd exec --stop-on-keyvault-error ./prod-deploy.sh

# Change working directory before execution
azd exec -C ./scripts ./run.sh

# Debug mode to see execution details
azd exec --debug ./troubleshoot.sh
```
16 changes: 16 additions & 0 deletions cli/src/internal/skills/skills.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package skills

import (
"embed"

"github.com/jongio/azd-core/copilotskills"
"github.com/jongio/azd-exec/cli/src/internal/version"
)

//go:embed azd-exec/SKILL.md
var skillFS embed.FS

// InstallSkill installs the azd-exec skill to ~/.copilot/skills/azd-exec.
func InstallSkill() error {
return copilotskills.Install("azd-exec", version.Version, skillFS, "azd-exec")
}
Loading