Google Cloud Functions on .NET


.NET for Google Cloud Functions (Alpha)

I spoke at many .NET conferences over the last 3-4 years and one of the top requests I always received was: When will .NET be supported on Cloud Functions?

Unfortunately, I didn’t have a good answer for a while. That all changed last month with the following tweet from Jon Skeet from our C# team:

.NET support is coming to Cloud Functions and it is currently public alpha. You need to sign up here to gain access.

In this blog post, I want to give the .NET support of Cloud Functions a try and see how it works. Along the way, we’ll also learn about Functions Framework that powers the .NET support of Cloud Functions under the covers.

All the code I’ll share here are already in my GitHub repo: cloud-functions-dotnetcore.

Why Cloud Functions

Before I dive into the .NET support of Cloud Functions, one question that’s worth answering is: Why Cloud Functions for .NET? There are already multiple ways of running .NET on Google Cloud, so why one more?

To recap the current options, you can run .NET Framework on Windows on Compute Engine or .NET Core containers on Linux in App Engine (Flex), Kubernetes Engine and most recently Cloud Run. Cloud Run has been my default choice to run .NET Core because of its fast deployments, its awesome DevEx and its serverless scaling and billing model. However, the unit of deployment in Cloud Run is a container. Going from .NET code to a Docker image via Dockerfile is usually straightforward but not trivial. Making sure that the Dockerfile is optimized and secure is even more work.

Sometimes, you just want to write code and deploy it without having to worry about containers. This is what Cloud Functions enable you to do. Cloud Functions also tend to have better integration with different Google Cloud events, as of today.

Functions Framework

Functions Framework tries to simplify writing and deploying portable functions across different platforms such as Cloud Functions, Cloud Run, Cloud Run on GKE or pure Knative. It’s also useful for local development and debugging. It supports HTTP and CloudEvent consuming functions.

Functions Framework for .NET does the same for .NET and it is the foundation for the .NET support in Cloud Functions.

Templates for Cloud Functions

Functions Framework for .NET comes with some templates for dotnet to make it easier to get started. Installing templates is a single command:

dotnet new -i Google.Cloud.Functions.Templates::1.0.0-alpha08

You’ll see the following new templates:

Templates                                                 Short Name             Language          Tags
--------------------------------------------------------------------------------------------------------------------------------
Google Cloud Functions CloudEvent Function                gcf-event              [C#], F#, VB      Google/Cloud/Functions/Events
Google Cloud Functions CloudEvent Function (Untyped)      gcf-untyped-event      [C#], F#, VB      Google/Cloud/Functions/Events
Google Cloud Functions HttpFunction                       gcf-http               [C#], F#, VB      Google/Cloud/Functions/Http

The first two for CloudEvent consuming functions and the last one is for HTTP consuming functions. We’ll explore all next.

HTTP Function

Creating an HTTP consuming function is as easy as:

dotnet new gcf-http

This creates a new project with a Function.cs file:

public class Function : IHttpFunction
{
    /// <summary>
    /// Logic for your function goes here.
    /// </summary>
    /// <param name="context">The HTTP context, containing the request and the response.</param>
    /// <returns>A task representing the asynchronous operation.</returns>
    public async Task HandleAsync(HttpContext context)
    {
        await context.Response.WriteAsync("Hello, Functions Framework.");
    }
}

This is the function that will handle HTTP requests and send HTTP responses back. I like the simplicity of the API and it’s a bonus that we don’t need to worry about containers.

To deploy the function, you’d use dotnet3 runtime and trigger-http flag:

gcloud functions deploy hello-http-function \
    --runtime dotnet3 \
    --trigger-http \
    --entry-point HelloHttpFunction.Function

In a minute or two, you should see the function deployed:

Hello HTTP .NET Cloud Function

CloudEvent Function

Functions Framework for .NET starts shining when you start consuming CloudEvents. The framework does a good job in not only parsing the CloudEvent but it also tries to parse the data within the CloudEvent into strong types.

For example, Google Cloud Storage emits CloudEvents. To consume those events, you’d first create a CloudEvent consuming function:

dotnet new gcf-event

This creates a Function.cs that parses a CloudEvent and also the data of the CloudEvent into a StorageObjectData object:

public class Function : ICloudEventFunction<StorageObjectData>
{
    public Task HandleAsync(CloudEvent cloudEvent, StorageObjectData data, CancellationToken cancellationToken)
    {

This is very useful! To deploy the function, you need to specify the trigger-event and trigger-resource accordingly:

gcloud functions deploy hello-cloudevent-storage-function \
    --runtime dotnet3 \
    --entry-point HelloCloudEventStorageFunction.Function \
    --trigger-event google.storage.object.finalize \
    --trigger-resource ${BUCKET_NAME} \
    --allow-unauthenticated

If you want to consume Pub/Sub messages instead, you can go through the same process but change StorageObjectData to MessagePublishedData in Function.cs to make sure the data is parsed for Pub/Sub message:

public class Function : ICloudEventFunction<MessagePublishedData>
{
    public Task HandleAsync(CloudEvent cloudEvent, MessagePublishedData data, CancellationToken cancellationToken)
    {

And you need to deploy with trigger-topic in this case:

gcloud functions deploy hello-cloudevent-pubsub-function \
    --runtime dotnet3 \
    --entry-point HelloCloudEventPubSubFunction.Function \
    --trigger-topic ${TOPIC_NAME}

You can see the full list of what triggers are supported in a strongly typed way in deployment page.

If you simply want to listen for CloudEvent without parsing the specific data type, you can create an untyped function as follows:

dotnet new gcf-untyped-event

This creates a Function.cs with a simple CloudEvent signature:

public class Function : ICloudEventFunction
{
    public Task HandleAsync(CloudEvent cloudEvent, CancellationToken cancellationToken)
    {

Dependency injection

The last thing I want to mention with the .NET support in Cloud Functions is its handy dependency injection.

For example, if you want the logger to be injection into your functions, you can simply add the following to your Function.cs:

private readonly ILogger _logger;

public Function(ILogger<Function> logger) =>
    _logger = logger;

In you need to do more complicated dependency injection, you can create a Startup.cs class extending FunctionsStartup and do additional injection in Configure method as shown in Function.cs:

public class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder) =>
        builder.Services
            .AddSingleton<IOperationSingleton, Operation>()
            .AddScoped<IOperationScoped, Operation>();
}

This wraps up my discussion of .NET on Cloud Functions. Exciting times for .NET on Google Cloud for sure!

  • Sign up for public alpha here.
  • Check out my code and instructions here.
  • Questions/comments? Reach out to me on Twitter (@meteatamel).

See also