Skip to content

An azd template using Bicep that shows multiple ways to call OAuth-protected backend APIs through Azure API Management. This template demonstrates three authentication scenarios: Credential Manager, send-request policy with client secret and send-request policy with client certificate (client assertion).

License

Notifications You must be signed in to change notification settings

ronaldbosma/call-apim-backend-with-oauth

Repository files navigation

Call API Management backend with OAuth

An Azure Developer CLI (azd) template using Bicep that shows multiple ways to call OAuth-protected backend APIs through Azure API Management. This template demonstrates three authentication scenarios: Credential Manager, send-request policy with client secret and send-request policy with client certificate (client assertion).

Overview

This template deploys the following resources:

Overview of the template

App registrations in Entra ID are created using the Microsoft Graph Bicep Extension that represent the backend, the client with certificate and the client with secret. A certificate and secret are created for the corresponding client app registration and stored in Azure Key Vault.

All authentication scenarios use the OAuth 2.0 Client Credentials Flow, which is ideal for server-to-server authentication where no user interaction is required.

An API Management service is deployed with two APIs:

We're using the BasicV2 tier because the Consumption tier doesn't support caching, which is important for token management. Additionally, Application Insights and Log Analytics Workspace are deployed for monitoring and logging purposes.

Important

This template is not production-ready; it uses minimal cost SKUs and omits network isolation, advanced security, governance and resiliency. Harden security, implement enterprise controls and/or replace modules with Azure Verified Modules before any production use.

Getting Started

Prerequisites

Before you can deploy this template, make sure you have the following tools installed and the necessary permissions.

Required Tools:

Required Permissions:

  • You need Owner permissions, or a combination of Contributor and Role Based Access Control Administrator permissions on an Azure Subscription to deploy this template.
  • You need Application Administrator or Cloud Application Administrator permissions to register the Entra ID app registrations. (You already have enough permissions if 'Users can register applications' is enabled in your Entra tenant.)

Optional Prerequisites:

To build and run the integration tests locally, you need the following additional tools:

Deployment

Once the prerequisites are installed on your machine, you can deploy this template using the following steps:

  1. Run the azd init command in an empty directory with the --template parameter to clone this template into the current directory.

    azd init --template ronaldbosma/call-apim-backend-with-oauth

    When prompted, specify the name of the environment (for example, oauthbackend). The maximum length is 32 characters.

  2. Run the azd auth login command to authenticate to your Azure subscription using the Azure Developer CLI (if you haven't already).

    azd auth login
  3. Run the az login command to authenticate to your Azure subscription using the Azure CLI (if you haven't already). This is required for the hooks to function properly. Make sure to log into the same tenant as the Azure Developer CLI.

    az login
  4. Run the azd up command to provision the resources in your Azure subscription and Entra ID tenant. This deployment typically takes around 4 minutes to complete.

    azd up

    See Troubleshooting if you encounter any issues during deployment.

  5. Once the deployment is complete, you can locally modify the application or infrastructure and run azd up again to update the resources in Azure.

Demo and Test

The Demo Guide provides a step-by-step walkthrough on how to test and demonstrate the deployed resources.

Clean up

Once you're done and want to clean up, run the azd down command. By including the --purge parameter, you ensure that the API Management service and Log Analytics workspace don't remain in a soft-deleted state, which could cause issues with future deployments of the same environment.

azd down --purge

Contents

The repository consists of the following files and directories:

├── .github                    
│   └── workflows              [ GitHub Actions workflow(s) ]
├── demos                      [ Demo guide(s) ]
├── hooks                      [ AZD Hooks to execute at different stages of the deployment process ]
├── images                     [ Images used in the README and demo guide ]
├── infra                      [ Infrastructure As Code files ]
│   ├── functions              [ Bicep user-defined functions ]
│   ├── modules                
│   │   ├── entra-id           [ Modules for all Entra ID resources ]
│   │   ├── services           [ Modules for all Azure services ]
│   │   └── shared             [ Shared Bicep modules ]
│   ├── types                  [ Bicep user-defined types ]
│   ├── main.bicep             [ Main infrastructure file ]
│   └── main.parameters.json   [ Parameters file ]
├── src                        
│   └── apis                   [ API Management API definitions ]
│       ├── protected-api      [ OAuth-protected backend API ]
│       └── unprotected-api    [ API demonstrating OAuth authentication methods ]
├── tests                      
│   ├── IntegrationTests       [ Integration tests for automatically verifying different scenarios ]
│   └── tests.http             [ HTTP requests to test the deployed resources ]
├── azure.yaml                 [ Describes the apps and types of Azure resources ]
└── bicepconfig.json           [ Bicep configuration file ]

Hooks

This template has several hooks that are executed at different stages of the deployment process. The following hooks are included:

Post-provision hooks

These PowerShell scripts are executed after the infrastructure resources are provisioned.

  • postprovision-create-and-store-client-certificate.ps1: Currently, we can't create certificates for an app registration with Bicep. This script creates a self-signed client certificate for the client app registration in Entra ID and stores it securely in Azure Key Vault. If the client certificate already exists in Key Vault, it won't create a new one.

  • postprovision-create-and-store-client-secret.ps1: Currently, we can't create secrets for an app registration with Bicep. This script creates a client secret for the client app registration in Entra ID and stores it securely in Azure Key Vault. If the client secret already exists in Key Vault, it won't create a new one.

  • postprovision-deploy-apis.ps1: The APIs are defined in a separate module from the infrastructure because the client secret and certificate must exist in Key Vault before deployment of the APIs. This script deploys the APIs to Azure API Management after the hooks that generate the client secret and certificate have completed.

Pre-down hooks

These PowerShell scripts are executed before the resources are removed.

  • predown-remove-app-registrations.ps1: Removes the app registrations created during the deployment process, because azd doesn't support deleting Entra ID resources yet. See the related GitHub issue: Azure/azure-dev#4724. The Entra ID resources have a custom tag azd-env-id: <environment-id>, so we can find and delete them.

Pipeline

This template includes a GitHub Actions workflow that automates the build, deployment and cleanup process. The workflow is defined in azure-dev.yml and provides a complete CI/CD pipeline for this template using the Azure Developer CLI.

GitHub Actions Workflow Summary

The pipeline consists of the following jobs:

  • Build, Verify and Package: This job sets up the build environment, validates the Bicep template and packages the integration tests.

  • Deploy to Azure: This job provisions the Azure infrastructure and deploys the packaged applications to the created resources.

  • Verify Deployment: This job runs automated integration tests on the deployed resources to verify correct functionality.

  • Clean Up Resources: This job removes all deployed Azure resources.

    By default, cleanup runs automatically after the deployment. This can be disabled via an input parameter when the workflow is triggered manually.

    GitHub Actions Manual Trigger

Setting Up the Pipeline

To set up the pipeline in your own repository, run the following command (add --provider azdo if you want to create an Azure DevOps pipeline):

azd pipeline config

Follow the instructions and choose Federated Service Principal (SP + OIDC), as OpenID Connect (OIDC) is the authentication method used by the pipeline, and only a service principal can be granted the necessary permissions in Entra ID.

After the service principal has been created:

  • Add the Microsoft Graph permissions Application.ReadWrite.All and AppRoleAssignment.ReadWrite.All to the app registration of the service principal, and grant admin consent for these permissions. Use the application permissions type, not delegated permissions type. These permissions are necessary to deploy the Entra ID resources with the Microsoft Graph Bicep Extension.
  • Assign the service principal either the Application Administrator or Cloud Application Administrator role if it's not already assigned. One of these roles is necessary for the hooks to successfully remove the Entra ID resources during cleanup.

For detailed guidance, refer to:

Tip

By default, AZURE_CLIENT_ID, AZURE_TENANT_ID and AZURE_SUBSCRIPTION_ID are created as variables in GitHub when running azd pipeline config. However, Microsoft recommends using secrets for these values to avoid exposing them in logs. The workflow supports both approaches, so you can manually create secrets and remove the variables if desired.

Note

In the GitHub Actions workflow, the environment name in the AZURE_ENV_NAME variable is suffixed with -pr{id} for pull requests. This prevents conflicts when multiple PRs are open and avoids accidental removal of environments, because the environment name tag is used when removing resources.

Integration Tests

The project includes integration tests built with .NET 10 that validate various scenarios through the deployed Azure services. The tests implement the same scenarios described in the Demo and are located in UnprotectedApiTests.cs. They automatically locate your azd environment's .env file if available, to retrieve necessary configuration. In the pipeline they rely on environment variables set in the workflow.

Troubleshooting

Cannot create API management account since another account is using the same apim service

If you've previously deployed this template, deleted the resources and then deployed it again shortly after, you may encounter the following error when redeploying the template. I believe this error occurs because resources for the Credential Manager scenario are deployed on shared infrastructure and are not entirely removed yet.

{
    "status": "Failed",
    "error": {
        "code": "DeploymentFailed",
        "target": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-oauthbackend-sdc-wiyuo/providers/Microsoft.Resources/deployments/oauthbackend-apis-1762513345",
        "message": "The resource write operation failed to complete successfully, because it reached terminal provisioning state 'Failed'.",
        "details": [
            {
                "code": "DeploymentFailed",
                "target": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-oauthbackend-sdc-wiyuo/providers/Microsoft.Resources/deployments/credentialManager-tzllwar74ckkc",
                "message": "At least one resource deployment operation failed. Please list deployment operations for details. Please see https: //aka.ms/arm-deployment-operations for usage details.",
                "details": [
                    {
                        "code": "AuthorizationGatewayBadRequestException",
                        "message": "Cannot create API management account cf-00000000000000000000000000000000-apim-oauthbackend-sdc-wiyuo since another account is using the same apim service name apim-oauthbackend-sdc-wiyuo."
                    }
                ]
            }
        ]
    }
}

To work around this issue, follow these steps:

  1. First run azd down --purge to remove the resources that were deployed.
  2. Change the environment name and/or region to ensure a unique API Management service name.
  3. Redeploy the template using azd up.

API Management deployment failed because the service already exists in soft-deleted state

If you've previously deployed this template and deleted the resources, you may encounter the following error when redeploying the template. This error occurs because the API Management service is in a soft-deleted state and needs to be purged before you can create a new service with the same name.

{
    "code": "DeploymentFailed",
    "target": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-oauthbackend-sdc-wiyuo/providers/Microsoft.Resources/deployments/apiManagement",
    "message": "At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/arm-deployment-operations for usage details.",
    "details": [
        {
            "code": "ServiceAlreadyExistsInSoftDeletedState",
            "message": "Api service apim-oauthbackend-sdc-wiyuo was soft-deleted. In order to create the new service with the same name, you have to either undelete the service or purge it. See https://aka.ms/apimsoftdelete."
        }
    ]
}

Use the az apim deletedservice list Azure CLI command to list all deleted API Management services in your subscription. Locate the service that is in a soft-deleted state and purge it using the purge command. See the following example:

az apim deletedservice purge --location "swedencentral" --service-name "apim-oauthbackend-sdc-wiyuo"

Deployment fails with BadRequest: ServiceManagementReference field is required for Update, but is missing in the request

In an enterprise environment (for tenants with Entra IDs enabled by Service Tree management), the ServiceManagementReference field on an application (app registration) is mandatory. If you're deploying this template in such an environment, you may encounter the following error during deployment:

ERROR: error executing step command 'provision': deployment failed: error deploying infrastructure: deploying to subscription:
Deployment Error Details:
BadRequest: ServiceManagementReference field is required for Update, but is missing in the request. 
Refer to the TSG `https://aka.ms/service-management-reference-error` for resolving the error 
Graph client request id: <request-id>. 
Graph request time: 2025-11-20T12:34:56.789Z.
TraceID: <trace-id>

This template provides an optional parameter to set the ServiceManagementReference field on app registrations if required by your tenant. Use the following command to set the AZURE_SERVICE_MANAGEMENT_REFERENCE environment variable in your azd environment:

azd env set AZURE_SERVICE_MANAGEMENT_REFERENCE <id>

Replace <id> with the valid Service Tree ID. If you don't provide a valid ID, the deployment will fail with the following error: Value for ServiceManagementReference must be a valid GUID.

About

An azd template using Bicep that shows multiple ways to call OAuth-protected backend APIs through Azure API Management. This template demonstrates three authentication scenarios: Credential Manager, send-request policy with client secret and send-request policy with client certificate (client assertion).

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •