As part of my “day job” I spend a lot of time working with C# and VB.Net 4.X “legacy” projects doing upgrades, bugs fixes and moving applications to Azure. For the last couple of months I have been working on a project replacing Microsoft message queue(MSMQ) queues with Azure Storage Queues so the solution is easier to deploy in Azure.
The next phase of the project is to replace a number of Windows Services with Azure Queue Trigger and Timer Trigger functions. The aim is a series of small steps which we can test before deployment rather than major changes, hence the use of V1 Azure functions for the first release.
Silver Fox systems sells a Visual Studio extension which generates an HTTP Trigger VB.Net project. I needed Timer and Queue Trigger functions so I created C# examples and then used them to figure out how to build VB.Net equivalents
After quite a few failed attempts I found this sequence worked for me
Even though the target platform is not .NET 5.0 ignore this and continue.
Added Microsoft.NET.Sdk.Functions (make sure version 1.0.38)
Then unload the project and open the file.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>TimerClass</RootNamespace>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="1.0.38" />
</ItemGroup>
</Project>
Add the TargetFramework and AzureFunctionsVersion lines
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>TimerClass</RootNamespace>
<TargetFramework>net48</TargetFramework>
<AzureFunctionsVersion>v1</AzureFunctionsVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="1.0.38" />
</ItemGroup>
</Project>
At this point the project should compile but won’t do much, so update the class to look like the code below.
Imports System.Threading
Imports Microsoft.Azure.WebJobs
Imports Microsoft.Extensions.Logging
Public Class TimerTrigger
Shared executionCount As Int32
<FunctionName("Timer")>
Public Shared Sub Run(<TimerTrigger("0 */1 * * * *")> myTimer As TimerInfo, log As ILogger)
Interlocked.Increment(executionCount)
log.LogInformation("VB.Net TimerTrigger next trigger:{0} Execution count:{1}", myTimer.ScheduleStatus.Next, executionCount)
End Sub
End Class
Then add an empty hosts.json file (make sure “copy if newer” is configured in properties) to the project directory, then depending on deployment model configure the AzureWebJobsStorage and AzureWebJobsDashboard connection strings via environment variables or a local.settings.json file.

Blob Trigger Sample code
Imports System.IO
Imports System.Threading
Imports Microsoft.Azure.WebJobs
Imports Microsoft.Extensions.Logging
Public Class BlobTrigger
Shared executionCount As Int32
' This function will get triggered/executed when a new message is written on an Azure Queue called events.
<FunctionName("Notifications")>
Public Shared Async Sub Run(<BlobTrigger("notifications/{name}", Connection:="BlobEndPoint")> payload As Stream, name As String, log As ILogger)
Interlocked.Increment(executionCount)
log.LogInformation("VB.Net BlobTrigger processed blob name:{0} Size:{1} bytes Execution count:{2}", name, payload.Length, executionCount)
End Sub
End Class
HTTP Trigger Sample code
Imports System.Net
Imports System.Net.Http
Imports System.Threading
Imports Microsoft.Azure.WebJobs
Imports Microsoft.Azure.WebJobs.Extensions.Http
Imports Microsoft.Extensions.Logging
Public Class HttpTrigger
Shared executionCount As Int32
<FunctionName("Notifications")>
Public Shared Async Function Run(<HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route:=Nothing)> req As HttpRequestMessage, log As ILogger) As Task(Of HttpResponseMessage)
Interlocked.Increment(executionCount)
log.LogInformation($"VB.Net HTTP trigger Execution count:{0} Method:{1}", executionCount, req.Method)
Return New HttpResponseMessage(HttpStatusCode.OK)
End Function
End Class
Queue Trigger Sample Code
Imports System.Threading
Imports Microsoft.Azure.WebJobs
Imports Microsoft.Extensions.Logging
Public Class QueueTrigger
Shared ConcurrencyCount As Long
Shared ExecutionCount As Long
<FunctionName("Alerts")>
Public Shared Sub ProcessQueueMessage(<QueueTrigger("notifications", Connection:="QueueEndpoint")> message As String, log As ILogger)
Interlocked.Increment(ConcurrencyCount)
Interlocked.Increment(ExecutionCount)
log.LogInformation("VB.Net Concurrency:{0} Message:{1} Execution count:{2}", ConcurrencyCount, message, ExecutionCount)
' Wait for a bit to force some consurrency
Thread.Sleep(5000)
Interlocked.Decrement(ConcurrencyCount)
End Sub
End Class
As well as counting the number of executions I also wanted to check that >1 instances were started to process messages when the queues had many messages. I added a “queues” section to the hosts.json file so I could tinker with the options.
{
"queues": {
"maxPollingInterval": 100,
"visibilityTimeout": "00:00:05",
"batchSize": 16,
"maxDequeueCount": 5,
"newBatchThreshold": 8
}
}
The QueueMessageGenerator application inserts many messages into a queue for processing.

When I started the QueueTrigger function I could see the concurrency count was > 0
Timer Trigger Sample Code
Imports System.Threading
Imports Microsoft.Azure.WebJobs
Imports Microsoft.Extensions.Logging
Public Class TimerTrigger
Shared executionCount As Int32
<FunctionName("Timer")>
Public Shared Sub Run(<TimerTrigger("0 */1 * * * *")> myTimer As TimerInfo, log As ILogger)
Interlocked.Increment(executionCount)
log.LogInformation("VB.Net TimerTrigger next trigger:{0} Execution count:{1}", myTimer.ScheduleStatus.Next, executionCount)
End Sub
End Class
The source code for the C# and VB.Net functions is available on GitHub