Home Office Space

The last few months have been fairly busy to say the least! We welcomed our second son, Lachlan, into the world, and I also launched my start-up project, Home Office Space NZ.

Home Office Space is New Zealand only website, which focuses on bringing the best quality desks, chairs, accessories, electronics, and gadgets to the New Zealand work-from-home market. Our aim is to be the main one-stop-online-shop for all Kiwis who work from home. It’s definitely going to be an interesting ride!

Still a work in progress, but fun to get back to figuring out all of the fun new stuff with Google Ads, Merchant Accounts, Social Media Marketing, Analytics, and general SEO work. If anyone has any hints, feel free to shout out!

Home Office Space Website

Azure | Function App (C#) | PDF Generation

So for the last couple of weeks I’ve been working on an Azure function app in C#, that will take a payload of HTML & a file name, and then generate and save a PDF file in Blob storage.

I worked based off this tutorial (https://itnext.io/to-azure-functions-wkhtmltopdf-convert-html-to-pdf-9dc69bcd843b) with some additions that I needed to make which I’ll try and explain below.

I used VSCode, but you will also need an Azure Portal account, a Linix Basic (B1) App Service Plan, and an Azure Storage Account.

In the Storage Account, I created a Blob Container called “pdf” which is where all my PDF files generated are placed.

The full repository is found hereAzure Functions Core Tools.

Following on from the tutorial (assuming you have built and followed the tutorial above) which tells you how to install and add DinkToPdf to your project, and how to get these added to your Azure Portal via SSH, I had a couple of issues:

  • My function app ran once, then every subsequent request returned 503’s. This would happen when debugging locally using Postman, or in the Azure Portal. Not good.
  • After the first run locally I noticed I would get this “Qt: Could not initialize OLE (error 80010106)” error thrown in the console.
  • I detailed some of the errors I had here.

So it turns out I needed to use earlier versions in my .csproj file:

<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.13" />
<PackageReference Include="Microsoft.Extensions.Http" Version="3.1.13" />

These couldn’t be later than 3.1.13.

I added a Startup class, and had to use Dependency Injection to setup the SynchronizedConverter to be added here, instead initialized as per the tutorial (I’ve left the comments in the code as a reference).

public class Startup : FunctionsStartup
    {
        public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
        {
            FunctionsHostBuilderContext context = builder.GetContext();
            builder.ConfigurationBuilder.AddEnvironmentVariables();
        }

        public override void Configure(IFunctionsHostBuilder builder)
        {
            builder.Services.AddSingleton(typeof(IPdfConverter), new SynchronizedConverter(new PdfTools()));
        }
    }

And above the namespace:

[assembly: FunctionsStartup(typeof(pdfCreation.Startup))]

I also added the STAThread to the function ‘Html2Pdf’:

[STAThread]

And then in the Html2Pdf class, I added:

private readonly IPdfConverter pdfConverter;
public Html2Pdf(IPdfConverter pdfConverter)
{
    this.pdfConverter = pdfConverter;
}

Also, don’t forget to add your connection string under configuration / application settings within the function app in the Azure Portal.

In order to use the Connection String both locally and in production, without having to worry about fluffing around and mistakenly committing it to source control, I did the below:

private string ConnectionString(ILogger log, ExecutionContext context)
        {
            var config = new ConfigurationBuilder()
                .SetBasePath(context.FunctionAppDirectory)
                .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
                .AddEnvironmentVariables()
                .Build();

            var connString = config.GetConnectionString("ConnectionString");            

            if(connString == null){log.LogInformation("Connection String is null");}

            return connString;
        }

Just make sure your connection string in the Azure Portal is called “ConnectionString” and copy this from the AzureWebJobsStorage under the Application settings, and it should be golden.

Locally, I added a “local.settings.json” file to the root directory of the project (which you’ll see is excluded via .gitignore). The format for this is below:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true;",
    "AzureWebJobsDashboard": "UseDevelopmentStorage=true;",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet",
    "FUNCTIONS_EXTENSION_VERSION": "~3",
    "APPINSIGHTS_INSTRUMENTATIONKEY": "XXXXXX",
    "APPLICATIONINSIGHTS_CONNECTION_STRING": "InstrumentationKey=XXXXXX;IngestionEndpoint=https://YOUR_LOCATION...ETC",
    "ConnectionString": "DefaultEndpointsProtocol=https;AccountName=YOUR_STORAGE_ACCOUNT;AccountKey=YOUR_STORAGE_ACCOUNT_KEY;EndpointSuffix=core.windows.net"
  },
  "ConnectionStrings": {
    "ConnectionString": "DefaultEndpointsProtocol=https;AccountName=YOUR_STORAGE_ACCOUNT;AccountKey=YOUR_STORAGE_ACCOUNT_KEY;EndpointSuffix=core.windows.net"
  }
}

When running locally, the connection string will be read from this file, otherwise it will be read from the function app’s application settings.

Hopefully these additions help someone! If you need any clarification, just hit me up.