Recap
In Part 1 we introduced the concept of an AI‑enhanced CI/CD pipeline and outlined the high‑level architecture that will drive automated decision‑making throughout the software delivery lifecycle. We also touched on the key components—source control, build agents, artifact storage, deployment orchestrators, and AI inference services—that will form the foundation of our pipeline. Part 2 dives into the nuts and bolts: how to provision the underlying infrastructure, install and configure self‑hosted agents, secure secrets, and wire everything together with Azure DevOps and a Claude 4.6 / GPT‑5.4 agentic workflow.
Setting Up the CI/CD Environment
Choosing the Right Cloud Platform
While the same principles apply across Azure, AWS, or GCP, the tutorial below uses Azure because of its native integration with Azure DevOps, Azure Kubernetes Service (AKS), and Azure Key Vault—an ideal stack for secure, scalable, and AI‑ready pipelines. Azure’s Azure DevOps Self‑Hosted Agent support allows us to run workloads on VMs or containers that can be dynamically sized based on AI‑driven predictions.
Provisioning Infrastructure with Terraform
Terraform keeps our environment reproducible and version‑controlled. The following script creates a resource group, an AKS cluster for hosting our application, a Virtual Machine Scale Set (VMSS) for self‑hosted agents, and an Azure Key Vault for secrets. It also registers an Azure DevOps organization and project via the azurerm_devops provider, enabling the pipeline to authenticate against the DevOps REST APIs.
terraform {
required_version = ">= 1.5.0"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~>3.0"
}
azurerm_devops = {
source = "azure/azurerm-devops"
version = "~>0.1"
}
}
}
provider "azurerm" {
features {}
}
provider "azurerm_devops" {
organization_name = var.devops_org
personal_access_token = var.devops_pat
}
###########################
# 1️⃣ Resource Group
###########################
resource "azurerm_resource_group" "rg" {
name = "rg-ai-cicd"
location = var.location
}
###########################
# 2️⃣ Key Vault
###########################
resource "azurerm_key_vault" "kv" {
name = "kv-ai-cicd-${var.environment}"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
tenant_id = data.azurerm_client_config.current.tenant_id
sku_name = "standard"
purge_protection_enabled = false
enable_soft_delete = true
}
###########################
# 3️⃣ AKS Cluster
###########################
resource "azurerm_kubernetes_cluster" "aks" {
name = "aks-ai-cicd"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
dns_prefix = "aks-ai-cicd"
default_node_pool {
name = "default"
node_count = 2
vm_size = "Standard_DS3_v2"
}
identity {
type = "SystemAssigned"
}
tags = {
environment = var.environment
}
}
###########################
# 4️⃣ VMSS for Self‑Hosted Agents
###########################
resource "azurerm_linux_virtual_machine_scale_set" "agent_vms" {
name = "vmss-agent-${var.environment}"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
sku = "Standard_DS3_v2"
instances = 2
admin_username = "azureuser"
admin_ssh_key {
public_key = file(var.ssh_pub_key_path)
}
upgrade_mode = "Manual"
os_profile {
computer_name_prefix = "agent"
admin_username = "azureuser"
custom_data = file("${path.module}/scripts/install_agent.sh")
}
os_profile_linux_config {
disable_password_authentication = true
}
tags = {
environment = var.environment
role = "self-hosted-agent"
}
}
###########################
# 5️⃣ Azure DevOps Project
###########################
resource "azurerm_devops_project" "project" {
name = "AI-CICD"
description = "AI‑Enhanced CI/CD Pipeline with Intelligent Decision‑Making"
visibility = "private"
version_control = "Git"
work_item_template = "Agile"
process_template_id = "adcc42ab-9882-485e-a3ed-7678f2e8bfb6" # Scrum
}
data "azurerm_client_config" "current" {}
Run terraform init && terraform apply to provision the environment. The VMSS custom data script install_agent.sh will bootstrap the Azure DevOps agent on each VM.
Deploying Azure DevOps Self‑Hosted Agents
The install_agent.sh script is lightweight and idempotent. It downloads the latest agent package, registers the VM with the Azure DevOps organization, and configures the agent to run in a Docker container for isolation.
#!/usr/bin/env bash
set -euo pipefail
# Variables – injected via VMSS custom data
AGENT_POOL="ai-cicd-pool"
ORGANIZATION_URL="https://dev.azure.com/${DEVOPS_ORG}"
PAT="${DEVOPS_PAT}"
# Install Docker
apt-get update
apt-get install -y docker.io
systemctl enable --now docker
# Create agent working directory
AGENT_DIR="/opt/azure-devops-agent"
mkdir -p $AGENT_DIR
# Download the agent
AGENT_TAR="vsts-agent-linux-x64-2.210.0.tar.gz"
wget -O /tmp/$AGENT_TAR https://vstsagentpackage.azureedge.net/agent/2.210.0/vsts-agent-linux-x64-2.210.0.tar.gz
tar -xzf /tmp/$AGENT_TAR -C $AGENT_DIR
# Configure the agent
cd $AGENT_DIR
./config.sh --unattended \
--url $ORGANIZATION_URL \
--auth pat \
--token $PAT \
--pool $AGENT_POOL \
--runasservice
# Start the agent service
systemctl enable azuredevopsagent
systemctl start azuredevopsagent
When the VMs boot, they automatically register with the Azure DevOps pool ai-cicd-pool. The agent runs as a Windows Service (or systemd unit on Linux) and is ready to pick up jobs.
Integrating AI Decision‑Making Services
At the core of our pipeline is the AI inference layer that predicts resource requirements, selects deployment environments, and recommends test coverage. We’ll use the new Claude 4.6 Opus Agentic Workflow and GPT‑5.4 Pro Parallel Agents via their respective APIs. The inference service can be hosted in an Azure Function that receives a prompt, invokes the model, and returns structured JSON. The function is secured with Managed Identity and Key Vault integration.
Azure Function – AI Inference
<function>
<name>PredictResources</name>
<bindings>
<httpTrigger authLevel="function" methods="post" route="predict" />
<httpOutput statusCode="200" />
</bindings>
</function>
Function code (Python 3.11) uses OpenAI’s SDK (or Anthropic for Claude) to generate predictions.
import os, json, azure.functions as func
from openai import OpenAI
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
def main(req: func.HttpRequest) -> func.HttpResponse:
try:
payload = req.get_json()
codebase = payload.get("codebase", "")
workload = payload.get("workload", {})
# Prompt engineering for resource prediction
prompt = f"""
You are an AI DevOps advisor. Given the following codebase summary:
{codebase}
And the anticipated workload:
{json.dumps(workload)}
Predict the following:
1️⃣ Required CPU cores per instance
2️⃣ Required RAM per instance (GB)
3️⃣ Number of instances
4️⃣ Recommended environment (staging, prod, canary)
Return a JSON object with keys: cpu, ram, instances, env.
"""
response = client.chat.completions.create(
model="gpt-5.4-pro",
messages=[{"role":"user","content":prompt}],
temperature=0.0,
max_tokens=256
)
prediction = response.choices[0].message.content
# Simple regex extraction (in production use a proper parser)
return func.HttpResponse(prediction, status_code=200, mimetype="application/json")
except Exception as e:
return func.HttpResponse(str(e), status_code=500)
Deploy the function to Azure Functions Premium Plan for low latency and auto‑scale. The function is protected by a function key, which the CI pipeline will retrieve from Key Vault.
Securing the Pipeline with Key Vault and Managed Identities
All secrets—DevOps PAT, OpenAI API key, database passwords—are stored in Azure Key Vault. The self‑hosted agents use Managed Identity to read secrets at runtime, eliminating hard‑coded credentials. The following Azure CLI snippet shows how to grant the agent pool identity access to Key Vault.
az keyvault set-policy \
--name kv-ai-cicd-${environment} \
--object-id $(az ad sp show --id $(az devops configure --defaults organization=${DEVOPS_ORG}) --query objectId -o tsv) \
--secret-permissions get list
In the pipeline, we fetch secrets using the AzureKeyVault@2 task.
- task: AzureKeyVault@2
inputs:
connectedServiceName: 'AzureServiceConnection'
keyVaultName: 'kv-ai-cicd-$(environment)'
secretsFilter: 'devops-pat,openai-api-key'
env:
DEVOPS_PAT: $(devops-pat)
OPENAI_API_KEY: $(openai-api-key)
This ensures the pipeline never exposes secrets in logs.
Configuring CI Pipeline YAML for AI‑Enabled Builds
The Azure DevOps pipeline YAML orchestrates the entire flow: from code checkout to AI inference, build, test, and deployment. The pipeline is split into stages: Prepare, Predict, Build, Test, Deploy.
trigger:
- main
variables:
environment: 'staging'
agentPool: 'ai-cicd-pool'
functionUrl: 'https://ai-cicd-function.azurewebsites.net/api/predict'
functionKey: $(function-key)
stages:
- stage: Prepare
jobs:
- job: Checkout
pool: $(agentPool)
steps:
- checkout: self
- script: |
echo "Collecting codebase summary..."
git log -1 --pretty=format:"%s" > summary.txt
displayName: Generate Code Summary
- stage: Predict
jobs:
- job: ResourcePrediction
pool: $(agentPool)
steps:
- task: HttpClient@2
inputs:
method: POST
url: $(functionUrl)
headers: |
Content-Type: application/json
x-functions-key: $(functionKey)
body: |
{
"codebase": "$(cat summary.txt)",
"workload": {
"expected_requests_per_minute": 1200,
"peak_hours": "09:00-17:00"
}
}
displayName: Call AI Inference
name: PredictResources
- script: |
echo "Parsing AI response..."
cat $(Pipeline.Workspace)/PredictResources.json | jq '.'
echo "Setting pipeline variables..."
echo "##vso[task.setvariable variable=CPU]$(cat $(Pipeline.Workspace)/PredictResources.json | jq -r '.cpu')"
echo "##vso[task.setvariable variable=RAM]$(cat $(Pipeline.Workspace)/PredictResources.json | jq -r '.ram')"
echo "##vso[task.setvariable variable=Instances]$(cat $(Pipeline.Workspace)/PredictResources.json | jq -r '.instances')"
echo "##vso[task.setvariable variable=Env]$(cat $(Pipeline.Workspace)/PredictResources.json | jq -r '.env')"
displayName: Set Variables
- stage: Build
dependsOn: Predict
jobs:
- job: Build
pool: $(agentPool)
steps:
- task: Docker@2
inputs:
containerRegistry: '$(dockerRegistry)'
repository: '$(imageName)'
command: 'buildAndPush'
Dockerfile: Dockerfile
tags: |
$(Build.BuildId)
displayName: Build Docker Image
- stage: Test
dependsOn: Build
jobs:
- job: UnitTests
pool: $(agentPool)
steps:
- script: |
pytest tests/
displayName: Run Unit Tests
- job: IntegrationTests
pool: $(agentPool)
steps:
- script: |
pytest integration_tests/
displayName: Run Integration Tests
- stage: Deploy
dependsOn: Test
condition: succeeded()
jobs:
- job: Deploy
pool: $(agentPool)
steps:
- task: HelmDeploy@0
inputs:
connectionType: 'Azure Resource Manager'
azureSubscription: 'AzureServiceConnection'
azureResourceGroup: 'rg-ai-cicd'
kubernetesCluster: 'aks-ai-cicd'
command: upgrade
chartType: FilePath
chartPath: charts/myapp
releaseName: myapp-$(Env)
overrideValues: |
replicaCount=$(Instances)
resources:
limits:
cpu: $(CPU)
memory: $(RAM)Gi
displayName: Deploy to AKS
Key points:
- The
Predictstage calls the AI inference function and sets pipeline variables for CPU, RAM, instance count, and target environment. - The
Buildstage builds a Docker image and pushes it to Azure Container Registry. - The
Deploystage uses Helm to deploy the application to AKS, injecting the AI‑recommended resource limits and replica count.
Dynamic Resource Sizing Based on Workload Patterns
To support multiple environments through natural language, the AI inference can be extended to analyze historical telemetry. The following Python snippet demonstrates how the inference function could ingest Prometheus metrics and predict scaling thresholds.
import prometheus_client
from prometheus_client import CollectorRegistry, Gauge
def fetch_metrics():
registry = CollectorRegistry()
# Example: fetch CPU usage over last 30 days
cpu_gauge = Gauge('cpu_usage', 'CPU usage percentage', registry=registry)
# In a
Originally published at https://artificial-inteligence.phptutorial.co.in
Top comments (0)