Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Capabilities define what an Azure Developer CLI (azd) extension can do, from adding custom commands to hooking into the deployment lifecycle. This article shows you how to add capabilities to the Contoso Resource Tagger sample extension from the Build a sample extension quickstart. You can apply the same patterns to any extension.
Each capability requires two things: an entry in the capabilities array of your extension manifest, and the corresponding implementation in your extension code.
Note
azd extensions are currently in beta.
Available capabilities
azd extensions can declare the following capabilities:
custom-commands: Adds new commands and command groups toazdunder your extension's namespace. For example, the sample extension addsazd tagger show. Use this capability to expose tasks that users run directly from the command line.lifecycle-events: Subscribes to events thatazdraises as it runs, such aspreprovision,postprovision, orpostdeploy. Your extension runs custom logic at those points without the user calling it directly. For example, the sample extension checks for required tags onpreprovisionbefore any resources are created.service-target-provider: Registers a new deployment target soazdknows how to package and deploy a service to a host it doesn't support out of the box. A service target maps to thehostvalue inazure.yaml. For example, you might add a provider that deploys a service to a third-party platform or an internal hosting environment.framework-service-provider: Registers support for a language or framework soazdknows how to restore, build, and package that project type. This maps to thelanguagevalue inazure.yaml. For example, you might add build support for a language thatazddoesn't recognize by default.provisioning-provider: Replaces howazdprovisions infrastructure duringazd provisionandazd up. Instead of the built-in Bicep or Terraform flow, your extension defines what happens. For example, you might integrate a different infrastructure-as-code tool or a custom deployment API.validation-provider: Contributes checks to theazdvalidation pipeline that run against a project or environment. For example, you might verify that naming conventions, required tags, or security settings are in place before a deployment proceeds.mcp-server: Exposes your extension's functionality as Model Context Protocol (MCP) tools that AI agents, such as GitHub Copilot, can discover and call. For example, the sample extension can expose asuggest_tagstool. For more information, see Add an MCP server to an extension.metadata: Provides richer command and configuration metadata thatazduses to describe your extension, such as detailed command descriptions and configuration hints surfaced in help output and IntelliSense.
This article focuses on the two most common capabilities: custom commands and lifecycle events. For the mcp-server capability, see Add an MCP server to an extension. For complete details on the provider capabilities, see the extension framework reference.
Add custom commands
The custom-commands capability lets your extension register new commands under a namespace in azd. The sample extension already uses this capability for the azd tagger show command.
Declare the capability in
extension.yaml.capabilities: - custom-commandsBuild your commands by using the
azdext.NewExtensionRootCommandhelper, which registers the standardazdflags and environment variable handling so you don't have to declare them manually:import "github.com/azure/azure-dev/cli/azd/pkg/azdext" func NewRootCommand() *cobra.Command { rootCmd, extCtx := azdext.NewExtensionRootCommand(azdext.ExtensionCommandOptions{ Name: "tagger", Use: "tagger <command> [options]", Short: "Standardize and report Azure resource tags.", }) rootCmd.AddCommand(newShowCommand(extCtx)) // Add other subcommands here. return rootCmd }The helper returns an
*ExtensionContextthat exposes the resolved values of the standard flags, such asEnvironmentandOutputFormat. Pass the context into your subcommands and read from it inside theirRunEhandlers instead of redeclaring the standard flags.
Subscribe to lifecycle events
The lifecycle-events capability lets your extension run custom logic during project and service lifecycle events, such as preprovision or postdeploy. For the sample extension, use a preprovision event to verify that required tags are set before azd provisions any resources.
Declare the capability in
extension.yaml.capabilities: - custom-commands - lifecycle-eventsAdd a
listencommand to your extension.azdinvokes this command to establish the bidirectional connection used for events. Use theazdext.NewExtensionHostbuilder to register your event handlers:func newListenCommand() *cobra.Command { return &cobra.Command{ Use: "listen", Short: "Starts the extension and listens for azd events.", Hidden: true, RunE: func(cmd *cobra.Command, args []string) error { ctx := azdext.WithAccessToken(cmd.Context()) azdClient, err := azdext.NewAzdClient() if err != nil { return fmt.Errorf("failed to create azd client: %w", err) } defer azdClient.Close() host := azdext.NewExtensionHost(azdClient). WithProjectEventHandler( "preprovision", func(ctx context.Context, args *azdext.ProjectEventArgs) error { fmt.Printf("Verifying required tags for project: %s\n", args.Project.Name) // Add your tag validation logic here. return nil }, ) // Run blocks until azd closes the connection. if err := host.Run(ctx); err != nil { return fmt.Errorf("failed to run extension: %w", err) } return nil }, } }Register the
listencommand on your root command:rootCmd.AddCommand(newListenCommand())
When a user runs azd provision or azd up, azd invokes your extension and calls the preprovision handler before provisioning resources.
Filter service events
Service event handlers support optional filtering so you only handle specific service types. For example, you can handle the prepackage event only for Python container app services:
host := azdext.NewExtensionHost(azdClient).
WithServiceEventHandler(
"prepackage",
func(ctx context.Context, args *azdext.ServiceEventArgs) error {
fmt.Printf("Packaging service: %s\n", args.Service.Name)
return nil
},
&azdext.ServiceEventOptions{
Host: "containerapp",
Language: "python",
},
)
Rebuild and test
After you add a capability, rebuild the extension and test the new behavior:
If you're using the watcher, your changes rebuild automatically. Otherwise, build manually:
azd x buildTest the capability. For lifecycle events, run a command that triggers the event, such as
azd provision.