In this article, I’ll demonstrate how to deploy the .NET 7.0 Rest API with Managed Identity and integrate it with Azure SQL. As a result, you can access Azure SQL without requiring a password in your application. Cool setup, in your perception?
This is the next episode of the Network Series. Just to recap, in the Network series, I demonstrate how to expose Rest APIs using Azure API Management. Private endpoints are used to connect all dependencies.
As is usual, all source code can be found at the beginning of the article, which can be found here. If you don’t know how to set up a pipeline, all the instructions are provided below!
You must first deploy Azure components from the Network Series scenario before you can use the pipeline described in this article. To do so, navigate to the Rest API with Private Endpoint, exposed to API Management by Terraform to download Terraform sources and ready-to-use pipelines.
The deployment pipeline consists of four stages:
- Build .NET 7.0 app
- Run Entity Framework migration for Azure SQL DB
- Deploy .NET 7.0 application into Azure App Service
- API Management and Azure Function integration
So, first, you must configure variables in dotnet-sql-app-build-and-deploy.yml file. Set variables values where you find # sign. All names of the App Service and Azure SQL should be in the Azure portal after terraform script execution.
pool: Default
variables:
buildConfiguration: 'Release'
webAppName: '#WEB_APP_NAME#'
azureSubscription: '#SERIVCE_CONNECTION_NAME#'
apimResourceGroup: '#APIM_RG#'
apimName: '#APIM_NAME#'
apimServiceName: '#APIM_SERVICE_NAME#'
apimPath: '#APIM_SERVICE_PATH'
specificationUrl: 'https://#WEB_APP_NAME#.azurewebsites.net/swagger/v1/swagger.json'
serviceUrl: 'https://#WEB_APP_NAME#.azurewebsites.net'
specificationFormat: 'OpenApiJson'
productName: TodoProduct
productNameId: TodoProductId
projectPath: $(Build.SourcesDirectory)/TodoApi/TodoApi.csproj
databaseContext: MyDatabaseContext
connectionString: Server=tcp:#AZURE_SQL_SERVER_NAME#.database.windows.net;Authentication=Active Directory Default;Database=#AZURE_SQL_DB_NAME#;
Since every API Management variable in the Network Series is common, you can simply copy it from another pipeline. However, the route to OpenApiJson and the project name may vary, so be careful!
Take a look at the below parameter, you can find it on the top pipeline:
pool: Default
The Default pool in Azure DevOps is designed for self-hosted agents. This solution is isolated at the network level, so we require it. More information is available here if you click here: Rest API with Private Endpoint, exposed to API Management by Terraform.
Build .NET 7.0 app
So building .NET 7.0 app is a trivial thing, but in this case, are some tricky aspects:
- Executing Entity Framework migration – running in the Linux self-hosted agent. I described in more detail here: Execute entity framework migration on a Linux
- Authentication in the Azure SQL – authentication is made without a password, why? We will get there in a second.
I don’t want to repeat Entity Framework migration on Linux once, so I only mention that you need to use a bundle to make it execute on the Linux agent.
About the second part, of course, the answer is very simple, our App Service has Managed Identity turn on. So this identity is used to authenticate in the Azure SQL. Before you can use this feature, Managed Identity user must be added to the Azure SQL. You need to log in as administrator, and execute the below query:
CREATE USER [MI_NAME] FROM EXTERNAL PROVIDER;
ALTER ROLE db_datareader ADD MEMBER [MI_NAME];
ALTER ROLE db_datawriter ADD MEMBER [MI_NAME];
ALTER ROLE db_ddladmin ADD MEMBER [MI_NAME];
Replace MI_NAME with your application name, because the basic name of the Managed identity equals the service name which is used.
The connection string for Azure SQL DB looks like this:
Server=tcp:#SERVER_NAME#.database.windows.net;Authentication=Active Directory Default;Database=#DB_NAME#;
Please update #SERVER_NAME# and #DB_NAME# for proper values in the appsettings.json file. Take a look, at that on the authentication part, Active Directory Default is set. This indicates that we are using Managed Identity.
Run Entity Framework migration for Azure SQL DB on Linux self-hosted agent
As you probably know, executing the Entity Framework bundle, is very easy, look below:
- task: AzureCLI@2
inputs:
azureSubscription: '$(azureSubscription)'
scriptType: bash
scriptLocation: inlineScript
inlineScript: |
chmod +x $(Pipeline.Workspace)/SQLScripts/bundle
$(Pipeline.Workspace)/SQLScripts/bundle --connection "$(connectionString)"
First, the artefact from the build stage is downloaded, just after our bundle is executed. Pay attention to chmod +x, it set that $(Pipeline.Workspace)/SQLScripts/bundle is executable.
The full pipeline you can get from here.
Deploy .NET 7.0 application into Azure App Service
Azure App Service deployment is the easiest part of the pipeline. We only must ensure that previous steps are successful, you can do it by adding:
- stage: Deploy
displayName: 'Deploy web app'
dependsOn: Update_db
condition: succeeded()
When the above condition is satisfied, we can make a deployment:
strategy:
runOnce:
deploy:
steps:
- task: AzureWebApp@1
inputs:
azureSubscription: '$(azureSubscription)'
appType: 'webAppLinux'
appName: $(webAppName)
package: '$(Pipeline.Workspace)/drop/TodoApi.zip'
We need only three parameters to make it work:
- azureSubscription – name of our connection to the Azure Cloud
- webAppName – name of the Azure App Service component, created during infrastructure deployment
- package – path to an artefact with application build archive
API Management and Azure App Service integration
Before we update API Management, we have to wait for Azure App Service to update. I add 3 minutes of sleep before the APIM update will execute.
To deploy Azure App Service to the API Management we have to:
Get API revision
If we want to import new versions of the API, we need to set new revisions for each deployment.
currentApiRevision=$(az apim api revision list --resource-group $APIMRESOURCEGROUP --service-name $APIMNAME --api-id $APIMSERVICENAME | jq '.[0].apiRevision' )
parsedApiRevision=$(echo $currentApiRevision | tr -d '"')
nextApiRevision=$(($parsedApiRevision + 1))
Create/get product
Contains one or more APIs, defines a usage quota, and the terms of use.
az apim product create --resource-group $APIMRESOURCEGROUP --service-name $APIMNAME --product-id $PRODUCTNAMEID --product-name $d --subscription-required true --approval-required false --state "published"
Add our API to the product
az apim product api add --resource-group $APIMRESOURCEGROUP --service-name $APIMNAME --product-id $PRODUCTNAMEID --api-id $APIMSERVICENAME
Import API from OpenApi definition, hosted by Azure Function
az apim api import -g $APIMRESOURCEGROUP --service-name $APIMNAME --path $APIMPATH --service-url $SERVICEURL --specification-url $SPECIFICATIONURL --specification-format $SPECIFICATIONFORMAT --api-type http --api-revision $nextApiRevision --api-id $APIMSERVICENAME --subscription-required false
Create a release of the revision, to publish the newest changes.
az apim api release create --resource-group $APIMRESOURCEGROUP --service-name $APIMNAME --api-id $APIMSERVICENAME --release-id MyRelease_$nextApiRevision --api-revision $nextApiRevision
The above API Management deployment is valid for all Rest APIs for which you can expose OpenAPI documents, for example, swagger. You need to remember to give some time between deployment and updating your API because you need a new version of the API to be available.
I hope this article was useful for you 🙂 Check out more articles from this series:
- Rest API with Private Endpoint, exposed to API Management by Terraform
- Efficient terraform modules structure
- Build a docker image in a self-hosted agent running on Azure Container Instances
- Execute entity framework migration on a Linux
- Setup Azure DevOps with Self-hosted agents and VNET
- Deploy Azure Function and expose it using API Management
- Deploy sample App Service with Azure SQL and API Management integration – you just read it!
- Coming soon – Deploy sample App Service for Containers and API Management integration