The goal is to automatically publish each ASP.NET Core Controller as individual API in Azure API Management from Azure DevOps pipelines.
Swashbuckle can be used to generate OpenAPI Specification (OAS) of ASP.NET Core application. There is also dotnet tool called swagger that can be used to generate OAS from command line.
dotnet swagger tofile --output swagger.V1.json My.ApibinReleasenetcoreapp3.1My.Api.dll V1
Generated OAS can be used to import API to Azure API Management.
Here is how APIs are shown in Azure API management before doing any changes.
All this is cool, but it would be even better to have each API inside OAS as separate API in Azure API Management.
The generated OAS contains all APIs in single file.
"paths": {
"/api/Users": {...},
"/api/Users/search": {...},
"/api/Roles": {...}
}
Server address is also included in OpenAPI specification file.
"servers": [
{
"url": "http://localhost:10586/"
}
]
I built simple command line application called SwaggerCli that
In Users -endpoint example, Users API -specific OAS contains
"paths": {
"/": {...},
"/search": {...},
},
"servers": [
{
"url": "http://localhost:10586/api/Users"
}
]
All Roles -API specific data has been removed, and server url contains /api/Users path.
The API specific OAS files are generated in build pipeline, and published as build results.
Following changes were required in build pipeline:
dotnet tool install Swashbuckle.AspNetCore.Cli --version 5.6.2
Steps 5-7 as YAML:
- task: CmdLine@2
displayName: Create specs directory
inputs:
script: 'mkdir $(Pipeline.Workspace)specs'- task: DotNetCoreCLI@2
displayName: 'Generate OpenAPI spec document'
inputs:
command: custom
custom: swagger
arguments: 'tofile --output $(Pipeline.Workspace)specs$.V1.json $.ApibinReleasenetcoreapp3.1$.Api.dll V1'- task: DotNetCoreCLI@2
displayName: "Divide OpenAPI spec document to API specific documents"
inputs:
command: custom
custom: SwaggerClibinReleasenetcoreapp3.1SwaggerCli.dll
arguments: '--inputFilename $(Pipeline.Workspace)specs$.V1.json --outputPath $(Pipeline.Workspace)specs'- task: CmdLine@2
displayName: Delete original OpenAPI spec file
inputs:
script: 'del $(Pipeline.Workspace)specs$.V1.json'- task: PublishPipelineArtifact@1
displayName: "Publish Artifact: OpenAPI specs"
inputs:
targetPath: '$(Pipeline.Workspace)specs'
artifactName: 'OpenAPI'
For each API specific OAS, new task in Release pipeline was added:
- task: AzurePowerShell@4
displayName: Update API Management for Users API
inputs:
azureSubscription: '$'
scriptType: 'FilePath'
scriptPath: '$(Pipeline.Workspace)/Deployment/Scripts/Update-ApimOpenAPISpec.ps1'
scriptArguments: '-ResourceGroupName "$" -ServiceName "$" -ServiceUrl "$api/users" -ApiPath "api/users" -ApiName "Users" -SpecificationFilePath $(Pipeline.Workspace)OpenAPIUsers.$.json'
errorActionPreference: 'stop'
azurePowerShellVersion: 'latestVersion'
Task is calling PowerShell script to import OpenAPI specification into Azure API Management:
[CmdletBinding()]
Param(
[string] [Parameter(Mandatory=$true)] $ResourceGroupName,
[string] [Parameter(Mandatory=$true)] $ServiceName,
[string] [Parameter(Mandatory=$true)] $ServiceUrl,
[string] [Parameter(Mandatory=$true)] $ApiPath,
[string] [Parameter(Mandatory=$true)] $ApiName,
[string] [Parameter(Mandatory=$true)] $SpecificationFilePath
)$apiMgmtContext = New-AzApiManagementContext -ResourceGroupName $ResourceGroupName -ServiceName $ServiceName
$api = Get-AzApiManagementApi -Context $apiMgmtContext -Name $ApiName
if ($null -eq $api) {
$api = New-AzApiManagementApi -Context $apiMgmtContext -Name $ApiName -ServiceUrl $ServiceUrl -Path $ApiPath -Protocols @("https")
}Import-AzApiManagementApi -Context $apiMgmtContext `
-SpecificationPath $SpecificationFilePath `
-SpecificationFormat 'OpenApi' `
-Path $api.Path `
-ApiId $api.ApiId
Here is how APIs are shown in Azure API management after changes were made.
Now I can define multiple API Management Products and include selected APIs for each Product. This was not possible without importing each ASP.NET Core Controller into own API Management API.
Thanks Twitter user @domze for great article below, it helped a lot to accomplish the goal!
https://www.domstamand.com/automating-your-openapi-updates-to-api-management-through-your-cicd-pipeline/