feat: Add provisioning provider support to extension framework#7482
feat: Add provisioning provider support to extension framework#7482wbreza wants to merge 10 commits intoAzure:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR extends the existing azd gRPC extension framework to support extension-provided infrastructure provisioning providers, enabling azd core to resolve custom providers (by name) from IoC and invoke them over a bidirectional brokered stream.
Changes:
- Adds a new
ProvisioningServicegRPC stream + message envelope/manager to register and service provisioning providers from extensions. - Introduces a core-side adapter (
ExternalProvisioningProvider) to bridgeprovisioning.Providercalls to the extension over the broker (including progress streaming). - Relaxes
provisioning.ParseProvider()validation and addsinfra.configpass-through support viaprovisioning.Options.Config.
Reviewed changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| cli/azd/grpc/proto/provisioning.proto | Defines the provisioning bidi-stream protocol and shared message types. |
| cli/azd/pkg/azdext/provisioning_envelope.go | Implements MessageEnvelope operations for provisioning messages (including progress). |
| cli/azd/pkg/azdext/provisioning_manager.go | Extension-side manager that registers a provider and dispatches incoming provisioning requests to it. |
| cli/azd/pkg/azdext/provisioning.pb.go | Generated protobuf stubs for provisioning messages. |
| cli/azd/pkg/azdext/provisioning_grpc.pb.go | Generated gRPC client/server stubs for provisioning stream. |
| cli/azd/pkg/azdext/extension_host.go | Adds WithProvisioningProvider() and wires provisioning manager into the host lifecycle. |
| cli/azd/pkg/azdext/azd_client.go | Adds AzdClient.Provisioning() service accessor. |
| cli/azd/internal/grpcserver/provisioning_service.go | Server-side stream handler with capability check + IoC registration of external providers. |
| cli/azd/internal/grpcserver/external_provisioning_provider.go | Core adapter implementing provisioning.Provider over the broker stream. |
| cli/azd/internal/grpcserver/server.go (+ tests) | Registers the new provisioning gRPC service on the server. |
| cli/azd/cmd/container.go | Registers ProvisioningService in IoC. |
| cli/azd/cmd/middleware/extensions.go (+ coverage test) | Adds provisioning-provider to listen capabilities. |
| cli/azd/pkg/extensions/registry.go + validate_registry* | Adds/validates the new ProvisioningProviderCapability. |
| cli/azd/pkg/infra/provisioning/provisioning.go (+ test) | Relaxes provider parsing to accept extension-defined kinds. |
| cli/azd/pkg/infra/provisioning/provider.go | Adds Options.Config map[string]any for extension-specific config. |
| cli/azd/extensions/microsoft.azd.demo/* | Demonstrates registering and implementing a provisioning provider in the demo extension. |
jongio
left a comment
There was a problem hiding this comment.
Solid implementation - the MessageBroker + bidi stream pattern is clean and consistent with how service targets and framework services work. The plumbing (IoC, middleware, server registration, extension host API) is all well done.
My comments focus on forward compatibility (making it easier to extend this surface later) and a couple of UX gaps where extensions won't get the same treatment as built-in providers.
jongio
left a comment
There was a problem hiding this comment.
Adds extension-based provisioning providers through the existing MessageBroker + bidi stream pattern. Architecture is clean and consistent with service targets and framework services.
Issues to address:
- pkg/azdext/provisioning_manager.go:238 - onInitialize and onEnsureEnv return both a response and an error, unlike the other 6 handlers in the file
- internal/grpcserver/external_provisioning_provider.go:26-27 - envManager and env fields are stored but never read
jongio
left a comment
There was a problem hiding this comment.
Third pass. Two items from my Apr 9 review are still open (response+error in onInitialize/onEnsureEnv, unused envManager/env fields). Three new findings - design/API-level, not runtime bugs.
Introduces extensibility for custom provisioning providers (e.g., Pulumi, CDK, scripts) via the existing gRPC extension framework, following the same MessageBroker-based patterns used by service targets and framework services. New components: - provisioning.proto: bidirectional streaming RPC with 9 request/response pairs for Initialize, State, Deploy, Preview, Destroy, EnsureEnv, and Parameters operations with progress streaming support - ProvisioningEnvelope: MessageEnvelope implementation for broker integration - ProvisioningManager: extension-side manager with factory-based provider creation and handler dispatch - ProvisioningService: server-side gRPC handler with capability verification and IoC container registration - ExternalProvisioningProvider: core-side adapter implementing provisioning.Provider that delegates to extensions via the broker - ExtensionHost.WithProvisioningProvider(): fluent API for extensions to register custom provisioning providers Wiring changes: - Added ProvisioningProviderCapability to extension registry - Added to listenCapabilities in extension middleware - Registered ProvisioningService in IoC container and gRPC server - Extended AzdClient with Provisioning() accessor - Relaxed ParseProvider() to accept any provider kind string - Added Options.Config field for extension-specific configuration Resolves Azure#7465 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Review fixes: - Add empty provider name validation in registration - Fix swallowed structpb.NewStruct error in config conversion - Extract getProvider() helper to eliminate DRY violation in handlers - Add error context wrapping to all ExternalProvisioningProvider methods - Return empty slices instead of nil from PlannedOutputs/Parameters - Use direct indexing in convertFromProtoParameters - Consolidate Register() lock acquisitions into single scope Demo extension: - Add DemoProvisioningProvider to microsoft.azd.demo extension - Register as 'demo' provider with WithProvisioningProvider() - Add provisioning-provider to extension capabilities Test fixes: - Add ProvisioningProviderCapability to ValidCapabilities list - Update TestListenCapabilities assertion count - Add capability to validation test coverage Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rt tests Proto changes: - Add hint field to ProvisioningStateOptions for deployment lookup - Add name field to ProvisioningOptions for layer scoping - Add PlannedOutputs request/response messages Core changes: - Wire hint from StateOptions through adapter to extension - Wire Options.Name through convertToProtoOptions - Implement PlannedOutputs end-to-end (proto, envelope, manager, adapter) - Update ParseProvider doc comment to match current behavior - Add TODO comments for progress callback console integration - Add scope comment on preview resource-level changes Testing: - Add table-driven tests for all convert helper functions (17 test cases) Demo extension: - Implement PlannedOutputs in DemoProvisioningProvider Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Handler pattern: - onInitialize/onEnsureEnv now return nil on error, matching other handlers Cleanup: - Remove unused envManager/env fields from ExternalProvisioningProvider - Remove lazyEnv dependency from ProvisioningService constructor - Simplify factory function signature Proto completeness: - Add virtual_env field to ProvisioningOptions for multi-layer parameter resolution - Add reserved-for-future comments on ProvisioningDeploymentPreview fields Demo extension: - Remove misleading parameters/outputs from Preview() — only Summary is consumed Safety: - Error on duplicate provider registration (one provider per extension) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Refactored ProvisioningManager to follow the same multi-provider pattern used by ServiceTargetManager: Proto changes: - Add provider_name routing field to all post-Initialize request messages (State, Deploy, Preview, Destroy, EnsureEnv, Parameters, PlannedOutputs) Manager refactor: - Replace single provider/providerName with factories + instances maps - Register() stores factory by name, allows multiple registrations - getOrCreateProvider() with double-checked locking for lazy initialization - getProvider() for post-Initialize lookups by provider name - All handlers now route to correct provider via req.GetProviderName() Core adapter: - ExternalProvisioningProvider sets ProviderName on every request message Server cleanup: - Track all registered provider names (not just last) - Clean up all providers on stream close Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
0bb9746 to
96a2385
Compare
…documentation Critical fixes: - Register() now stores factory ONLY after successful server registration (eliminates race where stale factory remained on registration failure) - ensureStream() cleans up broker on handler registration failure (prevents gRPC stream resource leak) High-priority fixes: - Wrap ensureStream() errors in Register/Receive/Ready with context - Add nil response guard in Register() before dereferencing - Protect registeredProviderNames read with mutex during cleanup logging - Extract registerHandlers() helper to eliminate DRY violation (8 repetitive blocks) - Clear factories map in Close() alongside instances Medium-priority fixes: - Add nil guards in proto conversion loops (Outputs, Resources, Parameters) - Document why ComponentManager isn't used (different Initialize signature) - Document provider_name routing design in proto and onInitialize handler - Document intentional progress callback API difference vs core Provider - Note path validation as systemic gap (matches service target pattern) - Add test coverage tracking comment referencing Azure#7480 Low-priority fixes: - Use new(ProvisioningEnvelope) per Go 1.26 standard - Add doc comment on ProvisioningProviderFactory exported type Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jongio
left a comment
There was a problem hiding this comment.
Solid work - the provisioning provider integration follows the established patterns well. All prior feedback addressed. Two minor items below.
- Delete factory from map if SendAndWait fails, enabling retry with same name - Add VirtualEnv test case to convertToProtoOptions coverage Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jongio
left a comment
There was a problem hiding this comment.
Post-approval commit looks good - factory cleanup on registration failure and VirtualEnv test coverage both address my earlier comments correctly. Re-approving.
Minor notes for future iterations:
- Proto uses
stringfor parameter/output values where core types useany- considergoogle.protobuf.Valuewhen typed outputs are needed. Options.Mode(deploy vs destroy) isn't mapped to extensions yet - low impact since extensions know mode from which methods are called.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… conflict azd now reserves --debug as a global flag. Extensions receive debug state via AZD_DEBUG and AZD_EXT_DEBUG environment variables instead. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Azure Dev CLI Install InstructionsInstall scriptsMacOS/Linux
bash: pwsh: WindowsPowerShell install MSI install Standalone Binary
MSI
Documentationlearn.microsoft.com documentationtitle: Azure Developer CLI reference
|
jongio
left a comment
There was a problem hiding this comment.
All previous feedback addressed. Re-approving after the two post-approval commits (cspell fix + --debug flag removal).
What Changed
Extensions can now register custom provisioning providers via the azd extension framework -- enabling alternatives to the built-in Bicep and Terraform providers. An extension author calls
WithProvisioningProvider("myprov", factory)on theExtensionHost, and end users setinfra: { provider: myprov }inazure.yamlto use it.This follows the same architecture established by custom service targets and framework services: proto definition, MessageBroker-based bidirectional streaming, ExtensionHost fluent API, server-side gRPC handler, IoC container registration, and core-side adapter.
Extension author experience:
End user experience:
The full provisioning lifecycle is supported: Initialize, State, Deploy, Preview, Destroy, EnsureEnv, Parameters, and PlannedOutputs -- with in-band progress streaming for Deploy/Preview/Destroy operations. Multiple providers per extension are supported with request-level routing via
provider_name.A demo provisioning provider is included in
microsoft.azd.demofor reference.How It Works
The implementation mirrors the established service target and framework service extension patterns. A new
provisioning.protodefines a bidirectional streaming RPC with aProvisioningMessageoneof envelope containing all request/response pairs plus progress messages. TheProvisioningManageron the extension side uses a factories/instances two-map pattern for multi-provider support with lazy initialization and double-checked locking. On the server side,ProvisioningServicecreates aMessageBrokerper stream, registers providers into the IoC container as named transients, and routes requests viaprovider_namefields on each message. TheExternalProvisioningProvideradapter implements the coreprovisioning.Providerinterface and delegates all operations through the broker with full type conversion between proto and Go types.Key design decisions: project-level provider routing (not per-service like service targets),
Options.Configpass-through viagoogle.protobuf.Structfor extension-specific configuration, and forward-compatibility fields for multi-layer deployments (hint,name,virtual_env).Issue References
Resolves #7465
Resolves #7466
Resolves #7467
Resolves #7468
Resolves #7469
Resolves #7470
Resolves #7471
Resolves #7472
Resolves #7473
Resolves #7474
Resolves #7475
Resolves #7476
Resolves #7477
Resolves #7478
Resolves #7479
Resolves #7480
Resolves #7481
Resolves #7502
Notes
ProvisioningService,ProvisioningManager,ProvisioningEnvelope) are tracked in the issues above and will be added before marking ready for review.