Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions examples/V2App/.funcignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.git*
.vscode
local.settings.json
test
*.tests.ps1
61 changes: 61 additions & 0 deletions examples/V2App/HttpFunctions.psm1
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# HTTP Trigger functions using the V2 attribute-based programming model.
# No function.json files are needed — the worker indexes these automatically.

function HttpExample {
[AzFunction()]
param(
[HttpTrigger(AuthLevel = 'Anonymous', Methods = 'GET', 'POST', Route = 'hello')]
$Request
)

$name = $Request.Query['name']
if (-not $name) {
$name = $Request.Body.name
}

if ($name) {
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [System.Net.HttpStatusCode]::OK
Body = "Hello, $name!"
})
}
else {
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [System.Net.HttpStatusCode]::BadRequest
Body = 'Please pass a name on the query string or in the request body.'
})
}
}

function GetAzureVm {
[AzFunction(Name = 'GetAzureVm')]
param(
[HttpTrigger(AuthLevel = 'Function', Methods = 'GET')]
$Request
)

$vmName = $Request.Query['vmName']
$resourceGroup = $Request.Query['resourceGroup']

if ($vmName -and $resourceGroup) {
$vm = Get-AzVM -ResourceGroupName $resourceGroup -Name $vmName -ErrorAction SilentlyContinue
if ($vm) {
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [System.Net.HttpStatusCode]::OK
Body = ($vm | ConvertTo-Json -Depth 5)
})
}
else {
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [System.Net.HttpStatusCode]::NotFound
Body = "VM '$vmName' not found in resource group '$resourceGroup'."
})
}
}
else {
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [System.Net.HttpStatusCode]::BadRequest
Body = 'Please provide vmName and resourceGroup query parameters.'
})
}
}
23 changes: 23 additions & 0 deletions examples/V2App/QueueFunctions.psm1
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Queue Trigger with Queue Output using the V2 attribute-based programming model.

function ProcessQueueMessage {
[AzFunction()]
param(
[QueueTrigger(QueueName = 'input-queue', Connection = 'AzureWebJobsStorage')]
$QueueItem,

[QueueOutput(QueueName = 'output-queue', Connection = 'AzureWebJobsStorage')]
$OutputQueue
)

Write-Information "Processing queue message: $QueueItem"

# Transform the message and send to output queue
$result = @{
OriginalMessage = $QueueItem
ProcessedAt = (Get-Date).ToString('o')
Status = 'Processed'
} | ConvertTo-Json

Push-OutputBinding -Name OutputQueue -Value $result
}
40 changes: 40 additions & 0 deletions examples/V2App/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# V2 Programming Model Example App

This example demonstrates the **V2 attribute-based programming model** for Azure Functions with PowerShell. Functions are defined using PowerShell attributes — no `function.json` files are needed.

## Key Differences from V1

| Feature | V1 (Classic) | V2 (Attribute-based) |
|---|---|---|
| Function definition | `function.json` + `run.ps1` per function | `[AzFunction()]` attribute in `.psm1` files |
| File layout | One folder per function | Functions organized by domain in `.psm1` files |
| Binding configuration | JSON in `function.json` | Attributes on parameters |
| Indexing | Host reads `function.json` | Worker indexes via AST parsing |

## Functions Included

### HttpFunctions.psm1
- **HttpExample** — Anonymous HTTP trigger (GET/POST) with custom route `/hello`
- **GetAzureVm** — Function-level auth HTTP trigger that fetches an Azure VM

### TimerFunctions.psm1
- **CleanupJob** — Timer trigger that runs every 6 hours

### QueueFunctions.psm1
- **ProcessQueueMessage** — Queue trigger with queue output binding

## Running Locally

```powershell
cd examples/V2App
func start
```

## .funcignore

The `.funcignore` file excludes files from deployment (similar to `.gitignore`):
- `.git*` — Git metadata
- `.vscode` — Editor settings
- `local.settings.json` — Local-only settings
- `test` — Test directories
- `*.tests.ps1` — Test files
19 changes: 19 additions & 0 deletions examples/V2App/TimerFunctions.psm1
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Timer Trigger function using the V2 attribute-based programming model.

function CleanupJob {
[AzFunction(Name = 'CleanupJob')]
param(
[TimerTrigger(Schedule = '0 0 */6 * * *')]
$Timer
)

Write-Information "Cleanup job triggered at: $(Get-Date)"

if ($Timer.IsPastDue) {
Write-Warning 'Timer is running late!'
}

# Add your cleanup logic here, for example:
# Remove-Item -Path "$env:TEMP\my-app-cache\*" -Recurse -Force -ErrorAction SilentlyContinue
Write-Information 'Cleanup job completed.'
}
12 changes: 12 additions & 0 deletions examples/V2App/host.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": "2.0",
"logging": {
"logLevel": {
"default": "Information"
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[4.*, 5.0.0)"
}
}
7 changes: 7 additions & 0 deletions examples/V2App/local.settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"IsEncrypted": false,
"Values": {
"FUNCTIONS_WORKER_RUNTIME": "powershell",
"AzureWebJobsStorage": "UseDevelopmentStorage=true"
}
}
7 changes: 7 additions & 0 deletions examples/V2App/profile.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Azure Functions profile.ps1
#
# This profile.ps1 will get executed every cold start of the Function App.
# Use it to define helper functions, set environment variables, or run initialization logic.

# Uncomment the next line to enable Az module connectivity via managed identity:
# Connect-AzAccount -Identity
4 changes: 4 additions & 0 deletions examples/V2App/requirements.psd1
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@{
# Uncomment to use the Az module for Azure resource management:
# 'Az' = '12.*'
}
32 changes: 32 additions & 0 deletions src/Attributes/AzFunctionAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

using System;

namespace Microsoft.Azure.Functions.PowerShellWorker.Attributes
{
/// <summary>
/// Marks a PowerShell function for indexing as an Azure Function (V2 programming model).
/// Apply this attribute to the function's [CmdletBinding()] or param block.
/// </summary>
/// <example>
/// function HttpExample {
/// [AzFunction()]
/// param(
/// [HttpTrigger(AuthLevel = "Anonymous", Route = "hello")]
/// $Request
/// )
/// ...
/// }
/// </example>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class AzFunctionAttribute : Attribute
{
/// <summary>
/// Optional function name override. If not specified, the PowerShell function name is used.
/// </summary>
public string Name { get; set; }
}
}
29 changes: 29 additions & 0 deletions src/Attributes/CosmosDB/CosmosDBInputAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

namespace Microsoft.Azure.Functions.PowerShellWorker.Attributes
{
/// <summary>
/// Defines a Cosmos DB input binding.
/// </summary>
public sealed class CosmosDBInputAttribute : InputBindingBaseAttribute
{
/// <inheritdoc />
public override string BindingType => "cosmosDB";

/// <summary>The DatabaseName property.</summary>
public string DatabaseName { get; set; }
/// <summary>The ContainerName property.</summary>
public string ContainerName { get; set; }
/// <summary>The Id property.</summary>
public string Id { get; set; }
/// <summary>The PartitionKey property.</summary>
public string PartitionKey { get; set; }
/// <summary>The SqlQuery property.</summary>
public string SqlQuery { get; set; }
/// <summary>The PreferredLocations property.</summary>
public string PreferredLocations { get; set; }
}
}
27 changes: 27 additions & 0 deletions src/Attributes/CosmosDB/CosmosDBOutputAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

namespace Microsoft.Azure.Functions.PowerShellWorker.Attributes
{
/// <summary>
/// Defines a Cosmos DB output binding.
/// </summary>
public sealed class CosmosDBOutputAttribute : OutputBindingBaseAttribute
{
/// <inheritdoc />
public override string BindingType => "cosmosDB";

/// <summary>The DatabaseName property.</summary>
public string DatabaseName { get; set; }
/// <summary>The ContainerName property.</summary>
public string ContainerName { get; set; }
/// <summary>The CreateIfNotExists property.</summary>
public bool CreateIfNotExists { get; set; }
/// <summary>The PartitionKey property.</summary>
public string PartitionKey { get; set; }
/// <summary>The PreferredLocations property.</summary>
public string PreferredLocations { get; set; }
}
}
38 changes: 38 additions & 0 deletions src/Attributes/CosmosDB/CosmosDBTriggerAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

namespace Microsoft.Azure.Functions.PowerShellWorker.Attributes
{
/// <summary>
/// Defines a Cosmos DB trigger binding.
/// </summary>
public sealed class CosmosDBTriggerAttribute : TriggerBindingBaseAttribute
{
/// <inheritdoc />
public override string BindingType => "cosmosDBTrigger";

/// <inheritdoc />
public override string RequiredProperties => "DatabaseName,ContainerName";

/// <summary>The DatabaseName property.</summary>
public string DatabaseName { get; set; }
/// <summary>The ContainerName property.</summary>
public string ContainerName { get; set; }
/// <summary>The LeaseContainerName property.</summary>
public string LeaseContainerName { get; set; }
/// <summary>The CreateLeaseContainerIfNotExists property.</summary>
public bool CreateLeaseContainerIfNotExists { get; set; }
/// <summary>The LeaseContainerPrefix property.</summary>
public string LeaseContainerPrefix { get; set; }
/// <summary>The FeedPollDelay property.</summary>
public int FeedPollDelay { get; set; } = -1;
/// <summary>The StartFromBeginning property.</summary>
public bool StartFromBeginning { get; set; }
/// <summary>The MaxItemsPerInvocation property.</summary>
public int MaxItemsPerInvocation { get; set; } = -1;
/// <summary>The PreferredLocations property.</summary>
public string PreferredLocations { get; set; }
}
}
16 changes: 16 additions & 0 deletions src/Attributes/Durable/ActivityTriggerAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

namespace Microsoft.Azure.Functions.PowerShellWorker.Attributes
{
/// <summary>
/// Defines a Durable Functions activity trigger binding.
/// </summary>
public sealed class ActivityTriggerAttribute : TriggerBindingBaseAttribute
{
/// <inheritdoc />
public override string BindingType => "activityTrigger";
}
}
26 changes: 26 additions & 0 deletions src/Attributes/Durable/DurableClientAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

namespace Microsoft.Azure.Functions.PowerShellWorker.Attributes
{
/// <summary>
/// Defines a Durable Functions client input binding (starter).
/// </summary>
public sealed class DurableClientAttribute : InputBindingBaseAttribute
{
/// <inheritdoc />
public override string BindingType => "durableClient";

/// <summary>
/// The task hub name. Overrides the default task hub name from host.json.
/// </summary>
public string TaskHub { get; set; }

/// <summary>
/// The connection name for the durable storage backend.
/// </summary>
public string ConnectionName { get; set; }
}
}
16 changes: 16 additions & 0 deletions src/Attributes/Durable/EntityTriggerAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

namespace Microsoft.Azure.Functions.PowerShellWorker.Attributes
{
/// <summary>
/// Defines a Durable Functions entity trigger binding.
/// </summary>
public sealed class EntityTriggerAttribute : TriggerBindingBaseAttribute
{
/// <inheritdoc />
public override string BindingType => "entityTrigger";
}
}
16 changes: 16 additions & 0 deletions src/Attributes/Durable/OrchestrationTriggerAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

namespace Microsoft.Azure.Functions.PowerShellWorker.Attributes
{
/// <summary>
/// Defines a Durable Functions orchestration trigger binding.
/// </summary>
public sealed class OrchestrationTriggerAttribute : TriggerBindingBaseAttribute
{
/// <inheritdoc />
public override string BindingType => "orchestrationTrigger";
}
}
Loading