Blog Post

Apps on Azure Blog
4 MIN READ

Azure Functions OpenAPI Extension Update

justinyoo's avatar
justinyoo
Icon for Microsoft rankMicrosoft
Dec 23, 2022

Since the last update of v1.4.0, this Azure Functions OpenAPI extension has a lot of improvements. Here in this post, let's discuss what improvements have been made.

Update – v1.5.0

.NET 7 support for the isolated worker extension

According to the Azure Functions roadmap, Azure Functions isolated worker model has started supporting .NET 7. Therefore, this OpenAPI extension also starts supporting .NET 7 with the isolated worker model. The migration step is as easy as changing the target framework. Update your .csproj file and find the <TargetFramework> node, and change it from netcoreapp3.1 (.NET Core 3.1), net5.0 (.NET 5) or net6.0 (.NET 6), to net7.0 (.NET 7).

 

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
    <AzureFunctionsVersion>v4</AzureFunctionsVersion>
    <OutputType>Exe</OutputType>
    ...
  </PropertyGroup>
  ...
  <ItemGroup>
    ...
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.OpenApi" Version="1.5.0" />
  </ItemGroup>
  ...
</Project>

 

OpenAPI settings instance support out-of-the-box

There are many environment variables related to the OpenAPI extension. All of them are prefixed with OpenApi__ as the environment variables. These environment variables can now be read through the OpenApiSettings instance, injected directly from the extension package. So, all you need to do is to inject the instance.

Here's the sample code for the in-proc worker model:

 

// in-proc worker function
public class Function1
{
    private readonly OpenApiSettings _openapi;
    private readonly ILogger _logger;

    // Inject OpenApiSettings instance through the constructor.
    public Function1(OpenApiSettings openapi, ILogger<Function1> logger)
    {
        _openapi = openapi;
        _logger = logger;
    }
    ...
}

 

And here's the sample code for the isolated worker model:

 

// out-of-proc worker function
public class Function1
{
    private readonly OpenApiSettings _openapi;
    private readonly ILogger _logger;

    // Inject OpenApiSettings instance through the constructor.
    public Function1(OpenApiSettings openapi, ILoggerFactory loggerFactory)
    {
        _openapi = openapi;
        _logger = loggerFactory.CreateLogger<Function1>();
    }
    ...
}

 

GitHub Actions workflow support

You can now generate the OpenAPI document on-the-fly within the GitHub Actions workflow. You can find more details on this document. Here's the sample usage:

 

steps:
- name: Checkout the repository
  uses: actions/checkout@v2

- name: Setup .NET SDK 7 LTS
  uses: actions/setup-dotnet@v1
  with:
    dotnet-version: '7.x'

- name: Restore NuGet packages
  shell: pwsh
  run: |
    dotnet restore .

- name: Build solution
  shell: pwsh
  run: |
    dotnet build . -c Debug -v minimal

- name: Generate OpenAPI document
  id: oai
  uses: Azure/azure-functions-openapi-extension/actions/build-openapi@v1
  with:
    functionAppPath: 'bin/Debug/net7.0'
    requestUri: 'http://localhost:7071/api/openapi/v3.json'
    documentPath: 'generated'
    documentName: 'openapi.json'
    delay: '30'
    isRemote: 'false'

- name: Check generated OpenAPI document
    shell: pwsh
    run: |
      echo "Generated Document: ${{ steps.oai.outputs.generated }}"

      $json = Get-Content -Path ${{ steps.oai.outputs.generated }} | ConvertFrom-Json
      $result = $json.openapi -eq "3.0.1"
      echo "Check result: $result"

 

Generic CI/CD pipeline support

In addition to supporting GitHub Actions workflow, both PowerShell script and bash shell script are provided to support generic CI/CD pipelines, including Azure DevOps.

Here's the PowerShell script:

 

& $([Scriptblock]::Create($(Invoke-RestMethod https://aka.ms/azfunc-openapi/generate-openapi.ps1))) `
    -FunctionAppPath    <function app directory> `
    -BaseUri            <function app base URI> `
    -Endpoint           <endpoint for OpenAPI document> `
    -OutputPath         <output directory for generated OpenAPI document> `
    -OutputFilename     <OpenAPI document name> `
    -Delay              <delay in second between run function app and document generation> `
    -UseWindows         <switch indicating whether to use Windows OS or not>

 

And here's the bash shell script:

 

curl -fsSL https://aka.ms/azfunc-openapi/generate-openapi.sh \
    | bash -s -- \
        -p|--functionapp-path   <function app directory> \
        -u|--base-uri           <function app base URI> \
        -e|--endpoint           <endpoint for OpenAPI document> \
        -o|--output-path        <output directory for generated OpenAPI document> \
        -f|--output-filename    <OpenAPI document name> \
        -d|--delay              <delay in second between run function app and document generation>

 

Update – v2.0.0-preview1

Breaking changes

  • The OpenApiHttpTriggerAuthorization class has now become a property of the OpenApiConfigurationOptions class.
  • The property, IncludeRequestingHost of OpenApiConfigurationOptions has now become ExcludeRequestingHost, and its default value is false.

Azure Functions Easy Auth support

If your Azure Function app implements the Easy Auth feature for better AuthN scenarios, the authN'd claims can be directly used for Swagger UI and OpenAPI document generation through the OpenApiHttpTriggerAuthorization class.

Here's the sample code for the in-proc worker model:

 

// in-proc worker function
public class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        builder.Services.AddSingleton<IOpenApiConfigurationOptions>(_ =>
                        {
                            var options = new OpenApiConfigurationOptions()
                            {
                                ...
                                Security = new OpenApiHttpTriggerAuthorization(async req =>
                                {
                                    var result = default(OpenApiAuthorizationResult);

                                    // Add your custom authorisation logic like below
                                    var identities = req.Identities;

                                    return await Task.FromResult(result).ConfigureAwait(false);
                                }),
                            };

                            return options;
                        });
    }
}

 

And here's the sample code for the isolated worker model:

 

public class Program
{
    public static void Main()
    {
        var host = new HostBuilder()
            .ConfigureServices(services =>
            {
                services.AddSingleton<IOpenApiConfigurationOptions>(_ =>
                        {
                            var options = new OpenApiConfigurationOptions()
                            {
                                ...
                                Security = new OpenApiHttpTriggerAuthorization(async req =>
                                {
                                    var result = default(OpenApiAuthorizationResult);

                                    // Add your custom authorisation logic like below
                                    var identities = req.Identities;

                                    return await Task.FromResult(result).ConfigureAwait(false);
                                }),
                            };

                            return options;
                        });
            })
            .Build();

        host.Run();
    }
}

 

Tell us what you think

There are a bunch of improvements released in this new version. Also, We've started gradually upgrading the extension version to v2.0.0. So if you have any questions, issues, suggestions or something else, please let us know!

Updated Dec 22, 2022
Version 1.0
  • craigh_velrada's avatar
    craigh_velrada
    Copper Contributor

    justinyoo thanks for this! When can we expect to see some of this detail make its way into the official docs? For example, is great to see that the Swagger UI can be hidden/disabled in higher environments purely by the presence of the OpenApi_HideSwaggerUI environment variable, however I really had to dig to discover this.

  • ericswann's avatar
    ericswann
    Copper Contributor

    I upgraded an existing Isolated API project to this version and the API becomes unresponsive when doing so. No errors/failures/warnings/etc... The API just spins after this update and never responds with either a simple health request or the swagger UI. Any ideas what might cause this?