Summary
Create the server-side ProvisioningService gRPC handler in internal/grpcserver/provisioning_service.go. This component runs in the azd core process and handles the bidirectional stream with extension processes. It extracts JWT claims, verifies capabilities, creates a MessageBroker, and registers ExternalProvisioningProvider instances in the IoC container when extensions register their providers.
Parent Epic
Part of #7465 — Provisioning Providers in the AZD Extension Framework
Context
Pattern to Follow
ServiceTargetService in internal/grpcserver/service_target_service.go is the exact model:
- Extract extension claims from JWT in gRPC context
- Verify extension has the required capability
- Create
MessageBroker for the stream
- Register handler for the registration request type
- On registration: create adapter, register in IoC, send response
broker.Run(ctx) — block until stream closes
- Cleanup on stream close
Detailed Requirements
Struct
type ProvisioningService struct {
azdext.UnimplementedProvisioningServiceServer
container *ioc.NestedContainer
extensionManager *extensions.Manager
lazyEnv *lazy.Lazy[*environment.Environment]
providerMap map[string]*grpcbroker.MessageBroker[azdext.ProvisioningMessage]
providerMapMu sync.Mutex
}
Constructor
func NewProvisioningService(
container *ioc.NestedContainer,
extensionManager *extensions.Manager,
lazyEnv *lazy.Lazy[*environment.Environment],
) *ProvisioningService
Stream() Handler Flow
- Extract extension claims:
extensions.ClaimsFromContext(stream.Context())
- Verify capability: check claims include
ProvisioningProviderCapability
- Create
MessageBroker[ProvisioningMessage] with ProvisioningEnvelope
- Register handler for
RegisterProvisioningProviderRequest:
- Extract
name from request
- Resolve extension from
extensionManager
- Create
ExternalProvisioningProvider via factory
- Register as named transient in IoC:
container.RegisterNamedTransient(name, factory)
- Store broker in
providerMap[name]
- Return
RegisterProvisioningProviderResponse
- Call
broker.Run(ctx) — blocks until stream closes
- Cleanup: remove from
providerMap
IoC Registration Pattern
err := s.container.RegisterNamedTransient(providerName, func(
console input.Console,
prompter prompt.Prompter,
) provisioning.Provider {
env, _ := s.lazyEnv.GetValue()
return external.NewExternalProvisioningProvider(
providerName,
extension,
broker,
console,
prompter,
env,
)
})
Note: Uses RegisterNamedTransient (not Singleton) because the provisioning provider may need fresh instances per resolution.
Acceptance Criteria
Dependencies
Files
- Create:
internal/grpcserver/provisioning_service.go
Summary
Create the server-side
ProvisioningServicegRPC handler ininternal/grpcserver/provisioning_service.go. This component runs in the azd core process and handles the bidirectional stream with extension processes. It extracts JWT claims, verifies capabilities, creates a MessageBroker, and registersExternalProvisioningProviderinstances in the IoC container when extensions register their providers.Parent Epic
Part of #7465 — Provisioning Providers in the AZD Extension Framework
Context
Pattern to Follow
ServiceTargetServiceininternal/grpcserver/service_target_service.gois the exact model:MessageBrokerfor the streambroker.Run(ctx)— block until stream closesDetailed Requirements
Struct
Constructor
Stream() Handler Flow
extensions.ClaimsFromContext(stream.Context())ProvisioningProviderCapabilityMessageBroker[ProvisioningMessage]withProvisioningEnvelopeRegisterProvisioningProviderRequest:namefrom requestextensionManagerExternalProvisioningProvidervia factorycontainer.RegisterNamedTransient(name, factory)providerMap[name]RegisterProvisioningProviderResponsebroker.Run(ctx)— blocks until stream closesproviderMapIoC Registration Pattern
Note: Uses
RegisterNamedTransient(not Singleton) because the provisioning provider may need fresh instances per resolution.Acceptance Criteria
ProvisioningServicestruct embeddingUnimplementedProvisioningServiceServerNewProvisioningService()constructor with IoC-compatible signatureStream()extracts claims viaextensions.ClaimsFromContext()Stream()verifiesProvisioningProviderCapabilityMessageBrokerwithProvisioningEnvelopeRegisterProvisioningProviderRequestExternalProvisioningProviderand registers as named transientRegisterProvisioningProviderResponsebroker.Run(ctx)(blocking)providerMapon closeDependencies
Files
internal/grpcserver/provisioning_service.go