6.5. Infrastructure as Code
Infrastructure as Code (IaC) means configuring cloud services and infrastructure using code, rather than manually pointing and clicking in a web console.
The configuration lives in version-controlled code, right alongside the application or model code it supports which makes your infrastructure repeatable, auditable, and automated.
Why use IaC?
- Consistency – ensure every environment (dev, test, prod) is set up identically
- Automation – spin up or tear down infrastructure with a single command
- Speed – new environments for models, APIs, or experiments can be created in minutes
- Versioning – infrastructure changes are tracked in Git, just like application code
- Collaboration – analysts and engineers can share the same definitions for how systems should run
Example in Pricing
A pricing team wants to deploy a new CatBoost model into production:
- With IaC, the cloud resources (compute instance, container registry, storage, networking) are defined in code
- The same code can create a test environment for validation, and later a production environment for live deployment
- If more compute power is needed, the configuration file can be updated and reapplied - no manual reconfiguration required
Terraform
Terraform is one of the most popular IaC tools. It uses configuration files (.tf) to define infrastructure in a cloud-agnostic way.
Terraform can be installed via the terminal
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update
sudo apt install terraform
terraform -version
Then add the following to .gitignore - terraform can create some large files which may also contain secrets:
.terraform/
*.tfstate
.tfstate.
*.tfvars
Example Terraform File
The below Terraform code is saved in a file 'main.tf'
This provisions the following Azure services:
-
Resource Group
-
Azure Container Registry (ACR)
-
Log Analytics Workspace
-
Azure Container Apps Environment
-
Azure Container App
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~>3.70"
}
}
}
provider "azurerm" {
features {}
subscription_id = var.subscription_id
tenant_id = var.tenant_id
}
variable "subscription_id" {}
variable "tenant_id" {}
# ------------------------
# Locals
# ------------------------
locals {
rg_name = "demo-fastapi"
rg_location = "UK South"
}
# ------------------------
# Resource Group
# ------------------------
resource "azurerm_resource_group" "rg" {
name = local.rg_name
location = local.rg_location
}
# ------------------------
# Azure Container Registry
# ------------------------
resource "azurerm_container_registry" "acr" {
name = "pricingdemofastapiregistry"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
sku = "Basic"
admin_enabled = true
}
# ------------------------
# Log Analytics Workspace (needed for Container Apps env)
# ------------------------
resource "azurerm_log_analytics_workspace" "log" {
name = "pricingdemo-logs"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
sku = "PerGB2018"
retention_in_days = 30
}
# ------------------------
# Container App Environment
# ------------------------
resource "azurerm_container_app_environment" "env" {
name = "pricingdemo-env"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
log_analytics_workspace_id = azurerm_log_analytics_workspace.log.id
}
# ------------------------
# Container App (replaces Linux Web App)
# ------------------------
resource "azurerm_container_app" "app" {
name = "pricingdemo-fastapi"
container_app_environment_id = azurerm_container_app_environment.env.id
resource_group_name = azurerm_resource_group.rg.name
revision_mode = "Single"
template {
container {
name = "fastapi"
image = "${azurerm_container_registry.acr.login_server}/pricing-app:latest"
cpu = 0.5
memory = "1.0Gi"
}
}
registry {
server = azurerm_container_registry.acr.login_server
username = azurerm_container_registry.acr.admin_username
password_secret_name = "acr-pwd"
}
secret {
name = "acr-pwd"
value = azurerm_container_registry.acr.admin_password
}
ingress {
external_enabled = true
target_port = 80
traffic_weight {
percentage = 100
latest_revision = true
}
}
}
# ------------------------
# Outputs
# ------------------------
output "acr_login_server" {
value = azurerm_container_registry.acr.login_server
}
output "web_app_url" {
value = azurerm_container_app.app.latest_revision_fqdn
}
The code does the following:
1. Provider Configuration
- Configures Terraform to use the Azure provider.
- Sets the Azure subscription ID and tenant ID via variables, allowing Terraform to authenticate to the correct Azure environment.
2. Resource Group
- Creates a Resource Group, which is a container for all other Azure resources.
- Groups all resources together for easier management and billing.
3. Azure Container Registry (ACR)
- Creates a private container registry to store Docker images.
- Uses a basic SKU and enables admin access for Terraform to authenticate and push or pull images.
4. Log Analytics Workspace
- Sets up a workspace for collecting logs and metrics.
- Required for monitoring Container Apps environments.
- Logs are retained for a defined period (e.g., 30 days).
5. Container App Environment
- Creates a Container App Environment, which is a sandboxed environment for running Azure Container Apps.
- Links the environment to the Log Analytics workspace for observability.
6. Container App Deployment
- Deploys a FastAPI container within the Container App Environment.
- Specifies CPU and memory allocation for the container.
- Connects the app to the private container registry for pulling the Docker image.
- Uses secrets for securely storing registry credentials.
- Configures external ingress, exposing the app to the internet on a specified port.
- Sets the traffic routing so that all requests go to the latest app revision.
7. Outputs
- Provides the login URL of the container registry, so images can be managed.
- Provides the public URL of the deployed FastAPI app for use in CI/CD pipelines, testing, or integration with other systems.
Running Terraform
Initialise terraform in the project
terraform init
Plan the deployment
terraform plan
Execute the deployment plan
terraform apply
If all goes well this will provision your cloud services and deploy them.
The FastAPI example can then be queried (from any process):
import requests
# Convert DataFrame to "dataframe_split" JSON
payload = {
"dataframe_split": {
"index": df.index.tolist(),
"columns": df.columns.tolist(),
"data": df.values.tolist()
},
"model_version_uri": MODEL_VERSION_URI,
"databricks_token": DATABRICKS_TOKEN
}
url = "http://pricingdemo-fastapi-webapp.azurewebsites.net/predict"
response = requests.post(url, json=payload)