Add V2 attribute-based programming model (worker indexing)#1130
Draft
andystaples wants to merge 1 commit into
Draft
Add V2 attribute-based programming model (worker indexing)#1130andystaples wants to merge 1 commit into
andystaples wants to merge 1 commit into
Conversation
Implement the V2 programming model for Azure Functions PowerShell Worker, enabling function definitions via PowerShell attributes instead of function.json. Core indexing engine: - FunctionIndexer: AST-based scanner that discovers [AzFunction()] functions from .ps1/.psm1 files without executing code - AttributeToBindingConverter: Converts attribute ASTs to BindingInfo and raw binding JSON via reflection-based property discovery - Type accelerators registered for all binding attributes Attribute classes (30+): - Triggers: HttpTrigger, TimerTrigger, QueueTrigger, BlobTrigger, EventHubTrigger, CosmosDBTrigger, ServiceBusTrigger, EventGridTrigger, SignalRTrigger, OrchestrationTrigger, ActivityTrigger, EntityTrigger, GenericTrigger - Inputs: BlobInput, CosmosDBInput, TableInput, SignalRInput, DurableClient, GenericInputBinding - Outputs: HttpOutput, QueueOutput, BlobOutput, CosmosDBOutput, TableOutput, EventHubOutput, EventGridOutput, ServiceBusOutput, SignalROutput, GenericOutputBinding Validation: - Exactly one trigger per function - Unique binding names within a function - Required binding properties (e.g. Schedule for TimerTrigger) - Graceful error handling with per-file skip-and-continue Runtime integration: - RequestProcessor wires up indexing on FunctionLoadRequest - FunctionLoader/DependencyManager path resolution for V2 (app root, not per-function subdirectories) - Pipeline output leak warning in PowerShellManager - worker.config.json declares worker-indexing capability .funcignore support: - Parses .funcignore from app root (glob patterns, comments, blank lines) - Filters files and directories during recursive scanning - Supports *, **, and ? glob patterns Identity-based auth for E2E tests: - EventHubsHelpers/CosmosDBHelpers use DefaultAzureCredential - Constants updated for fullyQualifiedNamespace/accountEndpoint V2 E2E test app (test/E2E/TestFunctionAppV2/): - HTTP, Timer, Queue, Blob, EventHub, CosmosDB, Durable functions - Extension bundle v4, external Durable SDK V2 example app (examples/V2App/): - HTTP, Timer, Queue trigger examples with .funcignore Unit tests: 428 passing (90 new)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements the V2 attribute-based programming model for Azure Functions PowerShell Worker. Functions are defined using PowerShell attributes (e.g.
[AzFunction()],[HttpTrigger()]) directly in.psm1files — nofunction.jsonfiles needed. The worker indexes functions by parsing the AST at startup, without executing user code.What's included
Core indexing engine (
src/WorkerIndexing/):FunctionIndexer— Recursively scans.ps1/.psm1files, parses the AST for[AzFunction()]-decorated functions, and producesRpcFunctionMetadatafor the hostAttributeToBindingConverter— Converts attribute AST nodes toBindingInfoand raw binding JSON via reflection-based property discovery; handles implicit output bindings (e.g. HTTP Response), cardinality, data type mapping.funcignoresupport — Parses glob patterns from.funcignoreat the app root to exclude files/directories during scanning30+ attribute classes (
src/Attributes/):HttpTrigger,TimerTrigger,QueueTrigger,BlobTrigger,EventHubTrigger,CosmosDBTrigger,ServiceBusTrigger,EventGridTrigger,SignalRTrigger,OrchestrationTrigger,ActivityTrigger,EntityTrigger,GenericTriggerBlobInput,CosmosDBInput,TableInput,SignalRInput,DurableClient,GenericInputBindingHttpOutput,QueueOutput,BlobOutput,CosmosDBOutput,TableOutput,EventHubOutput,EventGridOutput,ServiceBusOutput,SignalROutput,GenericOutputBindingRequiredPropertiesfor validationValidation (in
FunctionIndexer.BuildFunctionMetadata):ScheduleonTimerTrigger,DatabaseName+ContainerNameonCosmosDBTrigger)Runtime integration:
RequestProcessorwires upFunctionIndexerforFunctionsMetadataRequestresponsesFunctionLoader/DependencyManagerpath resolution adapted for V2 (app root vs. per-function subdirectory)PowerShellManager.ExecuteUserCode()worker.config.jsondeclaresWorkerIndexingcapabilityE2E test infrastructure:
test/E2E/TestFunctionAppV2/— Full V2 test app with HTTP, Timer, Queue, Blob, EventHub, CosmosDB, and Durable functionsDefaultAzureCredential(identity-based auth) instead of connection stringsExample app (
examples/V2App/):.funcignore,README.mdwith V1-vs-V2 comparisonTests: 428 unit tests passing (90 new), 20/23 E2E tests passing
Remaining Gaps / Known Issues
create_new_worker_instructions.mdupdate is also pending.function.jsoncoexistence story: Currently V1 (function.json) and V2 (attributes) are mutually exclusive per app. There's no explicit guard preventing a mixed app — could lead to confusing behavior if someone has both.Design Decisions & Alternatives
Attributes vs.
Register-AzureFunctioncmdletThe chosen approach uses PowerShell class attributes parsed via the AST at load time (no code execution). An alternative would be a
Register-AzureFunctioncmdlet that users call imperatively:Why attributes were chosen:
Tradeoffs:
AST parsing vs. script execution for indexing
The indexer uses
System.Management.Automation.Language.Parser.ParseFile()to extract function definitions without executing them. This is a deliberate security choice — user code may have side effects, module imports, or credential access that shouldn't happen at indexing time. The tradeoff is that dynamically constructed attributes (e.g. splatting, variables in attribute arguments) are not supported.Implicit output bindings
HTTP triggers automatically get an implicit
Responseoutput binding (matching the host's behavior). If the user explicitly declares[HttpOutput()] $Response, the explicit binding wins. This matches the Python V2 model's behavior..funcignoreimplementationA simple custom glob-to-regex converter is used rather than adding a
Microsoft.Extensions.FileSystemGlobbingpackage dependency. The patterns in.funcignorefiles in the wild are simple (literal names,*wildcards,**recursive), so a full globbing library was unnecessary.GenericTrigger/GenericInputBinding/GenericOutputBindingThese "escape hatch" attributes allow users to define bindings for extension types not covered by the built-in attribute classes (e.g. Kafka, RabbitMQ). The user specifies
Type,Connection, and aPropertieshashtable that gets flattened into the raw binding JSON.Needs Further Investigation / Testing
Cardinalityproperty on triggers (e.g.EventHubTrigger) maps toMany/OneonBindingInfo. Need to verify the host correctly handles all cardinality values, especially for batch-mode triggers.DataTypeon bindings maps toString/Binary/Stream/Int— need to verify the host accepts all of these and that they correctly influence parameter marshaling.FunctionsMetadataRequestpath should handle this, but it needs E2E verification.AzureFunctions.PowerShell.Durable.SDK) works with V2 in E2E tests, but the full matrix of Durable patterns (sub-orchestrations, eternal orchestrations, continue-as-new) hasn't been tested with V2..psm1: Since multiple functions can live in one.psm1, need to verify that module-scoped$script:variables work correctly across function invocations and don't leak state..psm1files or large files, indexing time should be benchmarked. The.funcignoreandExcludedDirectoriesfiltering helps, but there's no caching across cold starts.GenericTriggerescape hatch works for arbitrary binding types, but we should verify it works with all extension bundle versions and custom extensions loaded outside the bundle.TableInputfilter expressions: TheFilterandTakeproperties onTableInputneed verification with the Tables extension — these may require specific OData filter syntax.SignalRInputbinding (connection info / negotiate) — need to test the full SignalR handshake flow with V2.