Photo par Thom Milkovic

Configuration de l'authentification Azure Open AI pour APIM avec Terraform

Utilisez les policy et les identités managées

Créé par Damien Aicheh le 30/05/2024 · 21 mins

Dans ce tutoriel, vous allez configurer la communication entre API Management (APIM) et Azure Open AI à l’aide d’une identité managée et Terraform !

Cas d’usage

Par défaut, pour accéder aux services Azure Open AI, vous devez utiliser une clé d’accès ainsi qu’un endpoint. Cependant, ce macanisme peut être exposé dans l’application client, ce qui constitue un risque pour la sécurité. Pour éviter cela, vous pouvez utiliser une identité managée pour vous authentifier auprès du service backend. Dans ce tutoriel, vous utiliserez Terraform pour créer une APIM, ajouter une API basée sur la documentation Swagger d’OpenAI, et attribuerez un rôle à l’APIM. Vous ajouterez également une policy à l’APIM pour utiliser son identité managée pour s’authentifier auprès de la ressource Azure Open AI.

## De quoi avez-vous besoin?

Pour pouvoir réaliser entièrement ce tutoriel vous aurez besoin de :

Initialisation du projet

Dans un nouveau dossier, créons un fichier provider.tf et déclarez le provider azurerm :

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "3.105.0"
    }
  }

  backend "local" {}
}

provider "azurerm" {
  features {
  }
}

Ensuite, créez un fichier variables.tf et déclarez les variables que vous utiliserez dans ce tutoriel :

variable "application" {
  default     = "app"
  description = "Name of the application"
  type        = string
}

variable "environment" {
  description = "The environment deployed"
  type        = string
  default     = "dev"
  validation {
    condition     = can(regex("(dev|stg|pro)", var.environment))
    error_message = "The environment value must be a valid."
  }
}

variable "region" {
  description = "Azure deployment region"
  type        = string
  default     = "we"
}

variable "location" {
  description = "Azure deployment location"
  type        = string
  default     = "westeurope"
}

variable "owner" {
  description = "The name of the project's owner"
  type        = string
  default     = "me"
}

variable "resource_group_name_suffix" {
  type        = string
  default     = "01"
  description = "The resource group name suffix"
  validation {
    condition     = can(regex("[0-9]{2}", var.resource_group_name_suffix))
    error_message = "The resource group name suffix value must be two digits."
  }
}

variable "tags" {
  type        = map(any)
  description = "The custom tags for all resources"
  default     = {}
}

Sur cette base, vous pouvez créer un fichier locals.tf et déclarer les locals que vous utiliserez dans ce tutoriel :

locals {
  resource_suffix           = [lower(var.environment), lower(var.region), substr(lower(var.application), 0, 3), substr(lower(var.owner), 0, 3), var.resource_group_name_suffix]
  resource_suffix_kebabcase = join("-", local.resource_suffix)

  chat_model_name               = "gpt-35-turbo"

  tags = merge(
    var.tags,
    tomap(
      {
        "Owner"       = var.owner,
        "Environment" = var.environment,
        "Region"      = var.region,
        "Application" = var.application
      }
    )
  )
}

Comme vous pouvez le voir, vous disposez d’un resource_suffix et d’un resource_suffix_kebabcase pour générer un nom unique pour vos ressources. De plus, comme le balisage des ressources est important, vous avez fusionné les tags par défaut avec les tags d’entrée.

Créez un fichier rg.tf et déclarez le groupe de ressources que vous utiliserez pour y mettre toutes les ressources :

resource "azurerm_resource_group" "this" {
  name     = format("rg-%s", local.resource_suffix_kebabcase)
  location = var.location
  tags     = local.tags
}

Déployer Azure API Management et Azure Open AI

Maintenant, vous devez créer une APIM et une API basée sur la documentation Swagger d’OpenAI. Le bloc identity dans ce code définit le type d’identité sur SystemAssigned, ce qui signifie qu’Azure gérera automatiquement l’identité de cette APIM.

Dans un fichier apim.tf, vous pouvez déclarer la ressource comme suit :

resource "azurerm_api_management" "this" {
  name                 = format("apim-%s", local.resource_suffix_kebabcase)
  location             = azurerm_resource_group.this.location
  resource_group_name  = azurerm_resource_group.this.name
  publisher_name       = "Me"
  publisher_email      = "admin@me.io"
  sku_name             = "Developer_1"
  tags                 = local.tags

  identity {
    type = "SystemAssigned"
  }
}

Ensuite, définissons une API basée sur la documentation Swagger d’OpenAI. Le bloc de code Terraform suivant configure une API dans l’APIM’. Le fichier de spécification se trouve dans la documentation officielle Azure Open AI Swagger ou dans le dépôt GitHub de ce tutoriel.

En supposant que le fichier Swagger se trouve dans un répertoire assets, vous pouvez utiliser le code suivant pour lire le fichier et l’importer dans l’APIM.

data "template_file" "open_ai_spec" {
  template = file("${path.module}/assets/api_spec/open_ai_spec.json")
}

Ensuite, vous pouvez ajouter l’API à l’aide du code suivant :

resource "azurerm_api_management_api" "open_ai" {
  name                = "api-azure-open-ai"
  resource_group_name = azurerm_resource_group.this.name
  api_management_name = azurerm_api_management.this.name
  revision            = "1"
  display_name        = "Azure Open API"
  path                = "openai"
  protocols           = ["https"]

  import {
    content_format = "openapi+json"
    content_value  =  data.template_file.open_ai_spec.rendered
  }
}

L’API est maintenant créée et importée dans l’APIM. Déployons la ressource Azure Open AI. Placez toutes les ressources liées à Azure Open AI dans un fichier open_ai.tf.

resource "azurerm_cognitive_account" "open_ai" {
  name                  = format("oai-%s", local.resource_suffix_kebabcase)
  location              = azurerm_resource_group.this.location
  resource_group_name   = azurerm_resource_group.this.name
  kind                  = "OpenAI"
  sku_name              = "S0"
  tags                  = local.tags
  custom_subdomain_name = format("oai-%s", local.resource_suffix_kebabcase)

  identity {
    type = "SystemAssigned"
  }
}

resource "azurerm_cognitive_deployment" "chat_model" {
  name                 = local.chat_model_name
  cognitive_account_id = azurerm_cognitive_account.open_ai.id
  model {
    format  = "OpenAI"
    name    = local.chat_model_name
    version = "0301"
  }

  scale {
    type     = "Standard"
    capacity = 5
  }
}

Lors du déploiement du service Azure Open AI, il est important de spécifier un domaine personnalisé pour pouvoir accéder correctement au service. Le custom_subdomain_name peut être le même nom que celui donné au service Azure Open AI.

L’étape suivante consiste à déclarer le service Azure Open AI en tant que service backend dans l’APIM:

resource "azurerm_api_management_backend" "open_ai" {
  name                = "azure-open-ai-backend"
  resource_group_name = azurerm_resource_group.this.name
  api_management_name = azurerm_api_management.this.name
  protocol            = "http"
  url                 = format("%sopenai", azurerm_cognitive_account.open_ai.endpoint)
}

Comme vous pouvez le voir, l’url est le endpoint du service Azure Open AI qui se termine par un / combiné avec le path openai.

S’authentifier avec une identité managée

Enfin, vous pouvez attribuer le rôle Cognitive Services OpenAI User à l’identité managée de l’APIM. Ce rôle lui donne les autorisations dont elle a besoin pour interagir avec le service Azure Open AI. Dans un fichier role.tf, vous pouvez déclarer l’attribution du rôle :

resource "azurerm_role_assignment" "this" {
  scope                = azurerm_cognitive_account.open_ai.id
  role_definition_name = "Cognitive Services OpenAI User"
  principal_id         = azurerm_api_management.this.identity[0].principal_id
}

Vous pouvez ensuite ajouter une policy à l’API pour utiliser son identité managée pour vous authentifier auprès du service Azure Open AI. Ce bloc XML ci-dessous définit plusieurs policy sur la façon dont les requêtes et les réponses doivent être traitées.

<policies>
    <inbound>
        <set-backend-service backend-id="azure-open-ai-backend" />
        <authentication-managed-identity resource="https://cognitiveservices.azure.com" output-token-variable-name="msi-access-token" ignore-error="false" />
        <set-header name="Authorization" exists-action="override">
            <value>@("Bearer " + (string)context.Variables["msi-access-token"])</value>
        </set-header>
        <base />
    </inbound>
    <backend>
        <forward-request />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

La policy set-backend-service spécifie le service backend à utiliser pour l’API, dans ce scénario, le service Azure Open AI.

La policy authentication-managed-identity spécifie que votre APIM doit utiliser son identité managée pour s’authentifier auprès du service Azure Open AI. La policy set-header ajoute un header d’Authorization à chaque requête, en utilisant l’access token obtenu à partir de l’authentification de l’identité managée. L’élément base dans chaque section inclut les politiques par défaut pour ces sections. Pour plus de détails concernant cette policy, consultez la documentation officielle.

En supposant que cette stratégie se trouve dans le répertoire assets, vous pouvez utiliser le code suivant pour lire le fichier et l’importer dans l’APIM.

data "template_file" "global_open_ai_policy" {
  template = file("${path.module}/assets/policies/global_open_ai_policy.xml")
}

Enfin, vous pouvez appliquer cette policy à l’API à l’aide du code suivant :

resource "azurerm_api_management_api_policy" "global_open_ai_policy" {
  api_name            = azurerm_api_management_api.open_ai.name
  resource_group_name = azurerm_resource_group.this.name
  api_management_name = azurerm_api_management.this.name

  xml_content = data.template_file.global_open_ai_policy.rendered
}

Exécuter Terraform

Initialisons Terraform et appliquons-le :

terraform init
terraform plan --out=plan.out

Puis:

terraform apply plan.out

Cela prendra quelques minutes pour créer toutes les ressources. Assurez-vous de configurer le backend Terraform pour enregistrer le fichier de state à distance dans un emplacement sécurisé tel qu’un Azure Storage Account.

Tester API

Pour tester l’API, vous pouvez utiliser Azure API Management, sélectionner l’API Open AI et dans l’onglet Test, vous pouvez tester l’API. Si vous utilisez l’option Trace, vous pouvez voir les détails de la requête et de la réponse avec un Bearer Token ajouté à la requête.

Voici un exemple de paramètres à passer pour tester l’API :

deployment-id: gpt-35-turbo api-version: 2024-02-01

{
    "temperature": 1,
    "top_p": 1,
    "stream": false,
    "stop": null,
    "max_tokens": 2000,
    "presence_penalty": 0,
    "frequency_penalty": 0,
    "logit_bias": {},
    "user": "user-1234",
    "messages": [
        {
            "role": "system",
            "content": "You are an AI assistant that helps people find information"
        },
        {
            "role": "user",
            "content": "When Microsoft company was created?."
        }
    ],
    "n": 1
}

et vous aurez ce genre de réponses:

{
    "choices": [{
        "content_filter_results": {
            "hate": {
                "filtered": false,
                "severity": "safe"
            },
            "self_harm": {
                "filtered": false,
                "severity": "safe"
            },
            "sexual": {
                "filtered": false,
                "severity": "safe"
            },
            "violence": {
                "filtered": false,
                "severity": "safe"
            }
        },
        "finish_reason": "stop",
        "index": 0,
        "message": {
            "content": "Microsoft was created on April 4, 1975.",
            "role": "assistant"
        }
    }],
    "created": 1717078187,
    "id": "chatcmpl-9UalPYoIMtfCe2d0LSavtobQX3RUk",
    "model": "gpt-4",
    "object": "chat.completion",
    "prompt_filter_results": [{
        "prompt_index": 0,
        "content_filter_results": {
            "hate": {
                "filtered": false,
                "severity": "safe"
            },
            "self_harm": {
                "filtered": false,
                "severity": "safe"
            },
            "sexual": {
                "filtered": false,
                "severity": "safe"
            },
            "violence": {
                "filtered": false,
                "severity": "safe"
            }
        }
    }],
    "system_fingerprint": null,
    "usage": {
        "completion_tokens": 12,
        "prompt_tokens": 27,
        "total_tokens": 39
    }
}

Touche finale

Dans ce tutoriel, vous avez appris à utiliser Terraform pour créer une APIM, une API basée sur la documentation Swagger d’OpenAI, et attribuer un rôle à l’APIM. Vous avez également ajouté une policy à l’API pour utiliser l’identité managée de l’APIM pour s’authentifier auprès de la ressource Azure Open AI. Vous trouverez le code source complet dans ce tutoriel dans le répertoire Github.

N'hésitez pas à me suivre sur pour ne pas rater mon prochain tutoriel !