Install-free .NET w/ Google Cloud

Daz Wilkin
Google Cloud - Community
3 min readFeb 20, 2019

Gotta love containers

There are the languages where you can start writing code, build|run the code and you’re off to the races in no time, e.g. Golang, Python, JavaScript.

Then there are the languages where you need to spend a bunch of time remembering all the infrastructure that’s needed *before* you can start building|running code, e.g. Java and .NET.

I wanted to help a developer stumped by some C# code using a Google API Client Library. I was reluctant to try to repro the issue because it feels (worse than it is) to get a .NET runtime.

Containers to the rescue:

docker run \
--rm \
--interactive \
--tty \
--volume=$PWD/dotnet:/dotnet \
--volume=$PWD/images:/images \
--volume=$PWD/secret:/secret \
--workdir=/dotnet \
microsoft/dotnet:2.2-sdk \
dotnet watch run

This is super useful! I was able to quite quickly reconstruct a .NET project (I think there’s probably an easier way to do this too), plonk in the Program.cs and get running.

Setup

Let’s be cute and write “Hello Freddie” in F# (.NET==.NET). Create a directory called fsharpand create Program.fs in it:

open System[<EntryPoint>]
let main argv =
printfn "Hello Freddie!"
0 // return an integer exit code

In the same directory, create a solution, called fsharp.fsproj:

<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.fs" />
</ItemGroup>
</Project>

NB Too clever… For F# you’ll need the <ItemGroup> and <Compile Include...> values. These aren’t necessary for C#.

Watch-Run It

docker run \
--rm --interactive \
--tty \
--volume=$PWD/fsharp:/dotnet \
--workdir=/dotnet \
microsoft/dotnet:2.2-sdk \
dotnet watch run
watch : Polling file watcher is enabled
watch : Started
Hello Freddie!
watch : Exited
watch : Waiting for a file to change before restarting dotnet...

The watch permits you to change Program.cs, save it and have the project rebuilt and rerun on-the-fly. Nice!

Stepping Back

I subsequently realized that the dotnet image will also create project structures for us. For our root directory:

docker run \
--rm \
--interactive \
--tty \
--volume=$PWD:/dotnet \
--workdir=/dotnet \
microsoft/dotnet:2.2-sdk \
dotnet new console -lang F# -o fsharp

Or:

docker run \
--rm \
--interactive \
--tty \
--volume=$PWD:/dotnet \
--workdir=/dotnet \
microsoft/dotnet:2.2-sdk \
dotnet new console -lang C# -o csharp

Google API Client Library

To write code that leverages Google API Client Libraries (and any other Nuget packages), we’ll need to add the relevant packages to our project. The dotnet image supports this too:

... dotnet add package Google.Apis.Auth --version 1.38.0
... dotnet add package Google.Apis.Drive.v3 --version 1.38.0.1491

NB For brevity I’ve omitted the docker run ... preamble to these commands. You need only ensure that the correct volume mapping is specified; for the project where you wish the libraries added.

Alternatively, you can manually reference the libraries in the project file. For example, to add Google’s Auth API to the F# project:

<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Google.Apis.Auth" Version="1.38.0" />
</ItemGroup>
</Project>

If you read my previous post “Getting Started with .NET on GCP”, you’ll recall that we can search the Nuget repo for all of these libraries, including Google.Apis.Auth:

Nuget: Google.Apis.Auth

The image supports restore, build, run and the watch run command that I’ve been using above.

OAuth

One small, obscurely (!?) documented trick is that the default OAuth flow launches the browser in order to capture the authentication code. When running .NET in a container, this flow may be broken.

An alternative flow is to prompt the user to visit a URL, copy and paste the code back into the code. This will work and I recommend this approach.

The common flow is:

credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore("DriveUploader.ListMyLibrary")
);

The flow that prompts the user to visit the URL and copy the code is a very small change:

credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore("DriveUploader.ListMyLibrary"),
new PromptCodeReceiver()
);

Conclusion

If you’re lazy like me and groan at the prospect of installing .NET to develop code, fret not! Containers facilitate software installation and Microsoft has done a good job here in providing an image that saves a bunch of work.

Tidy up

When you’re done with the .NET runtime:

docker image rm microsoft/dotnet:2.2-sdk

NB The tag will change with the SDK version.

--

--