Last reviewed: May 2026
Build the AWS services on the AI-102 exam with plain Terraform — one block at a time, each tied back to an exam domain. The same code works on OpenTofu.
By the end of this lab you'll have provisioned, with plain Terraform, the substrate of a RAG-style Azure AI application — an Azure OpenAI account with a Claude / GPT-class chat model deployment, an Azure AI Search service for the retrieval index, a Storage Account for source documents, and a Key Vault holding the API keys. Five blocks; the AI-102 reference architecture.
Drop the snippets into a single main.tf, run terraform init, then terraform apply step-by-step.
>= 1.5 or OpenTofu >= 1.6.az login).azurerm_cognitive_account creation requires this once).Azure AI Search Basic is the main bill at $75/month. Drop to Free tier (sku = "free") if you're cost-sensitive; the lab still demonstrates the architecture.
Standard Azure opener. AI-102 expects you to use eastus or westus for newest Azure OpenAI model availability — older regions don't get GPT-4 family quickly.
terraform {
required_version = ">= 1.5"
required_providers {
azurerm = { source = "hashicorp/azurerm", version = "~> 4.0" }
random = { source = "hashicorp/random", version = "~> 3.6" }
}
}
provider "azurerm" {
features {
key_vault {
purge_soft_delete_on_destroy = true
}
}
}
resource "random_id" "suffix" {
byte_length = 3
}
data "azurerm_client_config" "current" {}
locals {
tags = {
Project = "certlabpro-ai-102"
ManagedBy = "terraform"
}
}
resource "azurerm_resource_group" "main" {
name = "certlabpro-ai-102-rg"
location = "eastus"
tags = local.tags
}Azure OpenAI is the production GenAI engine for AI-102. We create the account (kind = "OpenAI") and deploy gpt-4o-mini as the chat model — the cheapest GPT-4-class model at the time of writing and the recommended AI-102 lab default. The deployment is the named binding of a specific model version to a quota allocation; you call it by name from application code.
The model.version = "2024-07-18" is the gpt-4o-mini release date — AI-102 tests model-version stability as a recurring concern (locking to a specific version vs. auto-upgrade). The exam favors explicit pinning for production reliability.
resource "azurerm_cognitive_account" "openai" {
name = "openai-ai102-${random_id.suffix.hex}"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
kind = "OpenAI"
sku_name = "S0"
custom_subdomain_name = "openai-ai102-${random_id.suffix.hex}"
identity {
type = "SystemAssigned"
}
tags = local.tags
}
resource "azurerm_cognitive_deployment" "chat" {
name = "gpt-4o-mini"
cognitive_account_id = azurerm_cognitive_account.openai.id
model {
format = "OpenAI"
name = "gpt-4o-mini"
version = "2024-07-18"
}
sku {
name = "Standard"
capacity = 10 # TPM in thousands; 10 = 10K tokens-per-minute quota
}
}Azure AI Search (the rebranded Cognitive Search) is the AI-102 retrieval engine for RAG patterns. We provision a Basic-tier service — it supports system-assigned managed identity, which is required for AI Search to securely call Azure OpenAI for the integrated vectorization feature (where AI Search calls embeddings models on your behalf during indexing).
The local_authentication_enabled = false setting is the AI-102 production-best-practice answer — force Entra ID auth, no admin keys flying around. The system-assigned managed identity is granted access to Azure OpenAI in the next step.
resource "azurerm_search_service" "main" {
name = "search-ai102-${random_id.suffix.hex}"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
sku = "basic"
replica_count = 1
partition_count = 1
local_authentication_enabled = false # Entra auth only
public_network_access_enabled = true
identity {
type = "SystemAssigned"
}
tags = local.tags
}
# Grant AI Search's managed identity Cognitive Services OpenAI User on the
// account — required for integrated vectorization (AI Search → OpenAI embeddings).
resource "azurerm_role_assignment" "search_to_openai" {
scope = azurerm_cognitive_account.openai.id
role_definition_name = "Cognitive Services OpenAI User"
principal_id = azurerm_search_service.main.identity[0].principal_id
}Source documents for RAG live in Azure Storage. AI Search indexers pull from the blob container, run through the OpenAI embeddings model for vectorization, and populate the search index. Granting AI Search's managed identity Storage Blob Data Reader on the storage account is the AI-102 password-less retrieval pattern.
With Storage + AI Search + OpenAI all wired via managed identity, the RAG substrate is in place. The application layer (the chat orchestrator that takes user queries, calls AI Search, then calls OpenAI with the retrieved context) is the code you write — Terraform doesn't help there; it gets the infrastructure right.
resource "azurerm_storage_account" "docs" {
name = "ai102docs${random_id.suffix.hex}"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
account_tier = "Standard"
account_replication_type = "LRS"
account_kind = "StorageV2"
https_traffic_only_enabled = true
min_tls_version = "TLS1_2"
allow_nested_items_to_be_public = false
tags = local.tags
}
resource "azurerm_storage_container" "source" {
name = "source"
storage_account_id = azurerm_storage_account.docs.id
container_access_type = "private"
}
resource "azurerm_role_assignment" "search_to_storage" {
scope = azurerm_storage_account.docs.id
role_definition_name = "Storage Blob Data Reader"
principal_id = azurerm_search_service.main.identity[0].principal_id
}AI-102 expects production applications to read endpoints and keys from Key Vault, not from environment variables or config files. We provision a Key Vault and store the OpenAI endpoint, deployment name, and AI Search endpoint as secrets. In production the app's managed identity would have Key Vault Secrets User on this vault to read them at startup.
This closes the AI-102 Implement and Integrate loop: every endpoint, every key, every service URL goes through Key Vault — single point of secrets rotation, single point of audit. Every additional AI-102 pattern (Document Intelligence, Translator, Custom Vision, Speech) wires the same way.
resource "azurerm_key_vault" "main" {
name = "kv-ai102-${random_id.suffix.hex}"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
tenant_id = data.azurerm_client_config.current.tenant_id
sku_name = "standard"
enable_rbac_authorization = true
soft_delete_retention_days = 7
tags = local.tags
}
resource "azurerm_role_assignment" "kv_admin_self" {
scope = azurerm_key_vault.main.id
role_definition_name = "Key Vault Administrator"
principal_id = data.azurerm_client_config.current.object_id
}
resource "azurerm_key_vault_secret" "openai_endpoint" {
name = "openai-endpoint"
value = azurerm_cognitive_account.openai.endpoint
key_vault_id = azurerm_key_vault.main.id
depends_on = [azurerm_role_assignment.kv_admin_self]
}
resource "azurerm_key_vault_secret" "openai_deployment" {
name = "openai-deployment"
value = azurerm_cognitive_deployment.chat.name
key_vault_id = azurerm_key_vault.main.id
depends_on = [azurerm_role_assignment.kv_admin_self]
}
resource "azurerm_key_vault_secret" "search_endpoint" {
name = "search-endpoint"
value = "https://${azurerm_search_service.main.name}.search.windows.net"
key_vault_id = azurerm_key_vault.main.id
depends_on = [azurerm_role_assignment.kv_admin_self]
}terraform destroy tears down everything. Notes:
purge_soft_delete_on_destroy = true in the provider features makes destroy actually purge.AI-102 covers more services this lab can't fit — Azure AI Document Intelligence (form recognition, custom extraction models), Azure AI Vision (custom image classification + object detection), Azure AI Language (CLU + custom QnA), Azure AI Speech (custom speech, neural voices, speech translation), Azure AI Bot Service, Azure AI Content Safety, and the entire Azure AI Studio prompt-flow design surface.
We stick to the Azure OpenAI + AI Search + Storage + Key Vault RAG substrate because it's the most-tested AI-102 architecture and the one every other AI-102 pattern composes on top of. Document Intelligence feeds RAG. Vision extracts content from images that get embedded into RAG. Bot Service is a frontend on top of this stack.
For the surfaces above, see the Browse and Editorial sections of this cert page.