Skip to content

(Insider Preview) New Programming Model

Shreya Batra edited this page Jul 5, 2023 · 3 revisions

The v2 programming model is designed to provide a development experience that is more familiar to PowerShell developers. The new programming model is currently not released, and timeline is TBD. You can use the following guidance to learn more about it, and test out a private local version.

Note that leveraging the v2 programming model will provide an improved and seamless way to create functions, with the underlying deployment, debugging, and monitoring experience remaining the same.

  • General Notes
  • Supported Today
  • Examples
  • Get Started

General Notes

  1. The only files that are scanned for functions are the ones in the function app root folder. Any other files may be used to contain helper functions, but function declarations must be at the root. EXAMPLE: bug-bash-app/functions.psm1 is scanned, but bug-bash-app/src/functions.psm1 will not be

  2. For the binding attributes, only named arguments are supported. Positional arguments will not work.

Comparing the v1 and v2 programming models

v1

function StripForPs1Example {  
    [Function()]  
    param(  
        [HttpTrigger()]  
        $Request,  
        $TriggerMetadata  
    )  
   
    $value =  ([HttpResponseContext]@{    
        StatusCode = [HttpStatusCode]::OK  
        Body = 'New Programming Model rules! (Hero executed)'    
    })    
    $value | Push-OutputBinding -Name Response  
}  

run.ps1

using namespace System.Net

# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)

# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."

$body = "Hello from PowerShell v1 programming model"

# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
    StatusCode = [HttpStatusCode]::OK
    Body = $body
})

function.json

{
  "bindings": [
    {
      "authLevel": "function",
      "type": "httpTrigger",
      "direction": "in",
      "name": "Request",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "Response"
    }
  ]
}

v2

[Function()]  
param(  
    [HttpTrigger()]  
    $Request,  
    $TriggerMetadata  
)     
$value =  ([HttpResponseContext]@{  
    StatusCode = [HttpStatusCode]::OK  
    Body = 'New Programming Model rules! (Hero executed)'  
})    
$value | Push-OutputBinding -Name Response  

Comparing the folder structure of v1 and v2:

The following is the folder structure for a function application containing three functions.

v1

  • .vscode
  • EventGridTrigger1
    • function.json
    • run.ps1
  • HttpTrigger1
    • function.json
    • run.ps1
  • TimerTrigger1
    • function.json
    • run.ps1
  • .funcignore
  • .gitignore
  • host.json
  • local.settings.json
  • profile.ps1
  • requirements.psd1

v2

  • .vsode
  • .funcignore
  • .gitignore
  • functions.ps1
  • host.json
  • local.settings.json
  • profile.ps1
  • requirements.psd1

As you can see, the folder structure of a function application using the v2 programming model is simpler and flat.

Supported Today

Triggers:

  • HTTP
  • Timer
  • EventGrid
  • EventHub
  • Orchestration
  • Activity

Output Bindings:

  • HTTP
  • EventHub

DurableClient is also supported!

You can also use a combination of InputBinding, OutputBinding, and AdditionalInformation to construct bindings of any other type. InputBinding and OutputBinding should be used to specify the type of binding, and AdditionalInformation allows you to add key-value pairs to that binding.

Examples

HTTP Trigger

using namespace System.Net  
using module AzureFunctions.PowerShell.SDK    
function HttpTriggerMinimal {  
    [Function()]  
    param(  
        [HttpTrigger()]  
        $Request,  
        $TriggerMetadata  
    )   
    $value =  ([HttpResponseContext]@{  
        StatusCode = [HttpStatusCode]::OK  
        Body = 'New Programming Model rules! (Hero executed)'  
    })    
    $value | Push-OutputBinding -Name Response  
}  

function HttpTriggerMaximal {  
    [Function(Name='Trig')]  
    [HttpOutput(Name='Resp')]  
    param(  
        [HttpTrigger(AuthLevel='anonymous', Methods=('Get', 'Post'), Route='rerouted')]  
        $Request,  
        $TriggerMetadata  
    )  
   
    $value =  ([HttpResponseContext]@{  
        StatusCode = [HttpStatusCode]::OK  
        Body = 'The Http trigger invocation was successful'  
    })  
   
    $value | Push-OutputBinding -Name Resp  
}  

Timer Trigger:

function TimerTriggerMinimal {  
    # Input bindings are passed in via param block.  
    param(  
        [TimerTrigger(Chron='0 */5 * * * *')]  
        $Timer  
    )    
    # Get the current universal time in the default string format  
    $currentUTCtime = (Get-Date).ToUniversalTime()     
    # The 'IsPastDue' property is 'true' when the current function invocation is later than scheduled.  
    if ($Timer.IsPastDue) {  
        Write-Host "PowerShell timer is running late!"  
    }    
    # Write an information log with the current time.  
    Write-Host "PowerShell timer trigger function ran! TIME: $currentUTCtime"  
}  

EventGrid

function EventGridTrigger() {  
    [Function()]  
    param(  
        [EventGridTrigger()]  
        $Request,  
        $TriggerMetadata  
    )    
    $value =  ([HttpResponseContext]@{  
        StatusCode = [HttpStatusCode]::OK  
        Body = 'The Http trigger invocation was successful'  
    })  
   
    $value | Push-OutputBinding -Name Response  
}  

EventHub

# NOTE: all 4 arguments are required.   
  
function EventHubTrigger() {  
    [Function()]  
    param(  
        [EventHubTrigger(EventHubName='', ConsumerGroup='', Cardinality='', Connection='')]  
        $Request,  
        $TriggerMetadata  
    ) 
   
    $value =  ([HttpResponseContext]@{  
        StatusCode = [HttpStatusCode]::OK    
        Body = 'The Http trigger invocation was successful'    
    })     
    
    $value | Push-OutputBinding -Name Response    
}         

# HttpOutput syntax:   # [HttpOutput(Name='<name>')]  # EventHubOutput syntax:  
 
# [EventHubOutput(Name='<binding name>', EventHubName='<event hub name>', Connection='<event hub connection>')]  

Durable

function DurableFunctionsHttpStart1 {  
    [Function()]  
    param(  
        [DurableClient(Name='starter')]  
        [HttpTrigger(AuthLevel='anonymous', Methods=('get', 'post'), Route='DurableStart')]  
        $Request,   
        $TriggerMetadata  
    )  
 
    $FunctionName = "DurableFunctionsOrchestrator1"  
    $InstanceId = Start-DurableOrchestration -FunctionName $FunctionName  
    Write-Host "Started orchestration with ID = '$InstanceId'"  

    $Response = New-DurableOrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId  

    Push-OutputBinding -Name Response -Value $Response  
}  

function DurableFunctionsOrchestrator1 {  
    [Function()]  
    param(  
        [OrchestrationTrigger()]  
        $Context  
    )  

    $output = @()  

    $output += Invoke-DurableActivity -FunctionName 'Hello1' -Input 'Tokyo'  
    $output += Invoke-DurableActivity -FunctionName 'Hello1' -Input 'Seattle'  
    $output += Invoke-DurableActivity -FunctionName 'Hello1' -Input 'London'  

    $output  
}  

function Hello1 {  
    [Function()]  
    param(  
        [ActivityTrigger()]  
        $name  
    )  
    "Hello $name!"  
}  

Generic Binding Syntax Following is an HTTP trigger written in the style of the generic, but use your imagination!

function GenericTrigger() {  
    [Function()]  
    [OutputBinding(Type='http', Name='Response')]  
    [AdditionalInformation(BindingName='', Name='', Value='')]  
    param(  
        [InputBinding(Type='httpTrigger')]  
        [AdditionalInformation(BindingName='Request', Name='authLevel', Value='anonymous')]  
        [AdditionalInformation(BindingName='Request', Name='methods', Value=('GET', 'POST'))]  
        $Request,  
        $TriggerMetadata  
    )  
}