Setup for creating Agentic AI to deploy ARO clusters using Terraform
There are several steps to getting your agentic AI created so it can be sophisticated enough to deploy and destroy clusters as needed. In order to accomplish this, we must create setup files within Azure, as well as create a parser.
What will you learn?
- How to create a setup file within Azure Red Hat® OpenShift®
- How to create a parser using OpenAI
What do you need before starting?
- Azure Red Hat OpenShift cluster
- Azure OpenAI model access
Creating the setup file within Azure
First, you will create the setup file and to do so, you would need your Azure credentials such as your Azure Client ID, Azure Client Secret, Azure Tenant ID, and Azure Subscription ID.
You can retrieve these credentials via Azure Portal or via az cli, the latter being the easier one. To do so via CLI, first run:
$ az account show
From the output, use the id value as your Azure Subscription ID, and tenantId as your Azure Tenant ID.
As for the other credentials, you can either use the existing Service Principal or create a new one. For the latter, run the following (give it a proper name and replace the subscription ID):
$ az ad sp create-for-rbac --name "YOUR-NEW-SP-NAME" --role contributor --scopes /subscriptions/YOUR-SUBSCRIPTION-ID
And if you choose to update an existing service principal:
$ az role assignment create --assignee “YOUR-APPLICATION-ID” --role "Contributor" --scope /subscriptions/YOUR-SUBSCRIPTION-ID
From the output, take the appId as your Azure Client ID and the password as your Azure Secret ID. Keep all these credentials handy for this step.
The setup here essentially is an environment bootstrapping module that handles dependency installation (Terraform, Azure CLI), configures Azure service principal credentials, and ensures the execution environment is properly initialized.
On the Red Hat OpenShift AI dashboard, launch a Jupyter notebook instance. To do so, on the navigation pane, click Application, then click Enabled, and then on the Jupyter tab, select Launch application.
In this example, you will be using TensorFlow 2025.1 image with Medium container size for the notebook. This might take a few minutes to provision.
And once the notebook is ready, go to the File tab on the upper left and choose New, and select Python File. Copy the lines below, save and rename the file as setup.py. Replace the env vars with your Azure credentials.
import os
import sys
from pathlib import Path
import subprocess
def setup_environment():
os.environ.update({
'AZURE_CLIENT_ID': 'YOUR-AZURE-CLIENT-ID',
'AZURE_CLIENT_SECRET': 'YOUR-AZURE-CLIENT-SECRET',
'AZURE_TENANT_ID': 'YOUR-AZURE-TENANT-ID',
'AZURE_SUBSCRIPTION_ID': 'YOUR-AZURE-SUBSCRIPTION-ID'
})
terraform_installed = install_terraform()
az_installed = install_azure_cli()
openai_installed = install_openai()
return {
"terraform_installed": terraform_installed,
"az_cli_installed": az_installed,
"openai_sdk_installed": openai_installed
}
def install_terraform():
local_bin = Path.home() / '.local' / 'bin'
local_bin.mkdir(parents=True, exist_ok=True)
os.environ['PATH'] = f"{local_bin}:{os.environ['PATH']}"
try:
subprocess.run(["terraform", "--version"], capture_output=True, text=True)
return True
except FileNotFoundError:
try:
subprocess.run(["wget", "-q", "https://releases.hashicorp.com/terraform/1.7.4/terraform_1.7.4_linux_amd64.zip"], check=True)
subprocess.run(["unzip", "-o", "terraform_1.7.4_linux_amd64.zip", "-d", str(local_bin)], check=True)
subprocess.run(["chmod", "+x", f"{local_bin}/terraform"], check=True)
subprocess.run(["rm", "terraform_1.7.4_linux_amd64.zip"], check=True)
return True
except:
return False
def install_azure_cli():
try:
subprocess.run(["az", "--version"], capture_output=True, text=True)
return True
except FileNotFoundError:
try:
subprocess.run([sys.executable, "-m", "pip", "install", "--quiet", "azure-cli"], check=True)
return True
except:
return False
def install_openai():
try:
import openai
return True
except ImportError:
try:
subprocess.run([sys.executable, "-m", "pip", "install", "--quiet", "openai"], check=True)
return True
except:
return False
if __name__ == "__main__":
setup_environment()
Creating the OpenAI parser
Next, let's create the parser which acts as the Natural Language Processing interface using an Azure OpenAI’s model, i.e. GPT-4o-mini. to extract structured parameters from unstructured text. That way, the agent will understand our prompts intelligently and convert it into technical parameters the system can use.
Here you will also set up the default parameters if not specified in the user prompts such as cluster name, region, worker node size, worker node count, version, and private/public. Note that some of these default parameters are slightly different from those of the Terraform repository. For example, the default region here is westus and the default cluster name is agentic-aro, so feel free to adjust these parameters accordingly. Also note that it will spin up the latest version if users do not specify it in the prompt.
Create a new Python file called parser.py and copy the below code and save it.
import os
import json
import re
from typing import Dict, Any
from openai import AzureOpenAI
class AzureOpenAIParser:
def __init__(self,
azure_endpoint=None,
api_key=None,
api_version="2024-12-01-preview",
deployment_name="gpt-4o-mini",
debug=False):
self.debug = debug
self.deployment_name = deployment_name
self.azure_endpoint = azure_endpoint or os.environ.get('AZURE_OPENAI_ENDPOINT')
self.api_key = api_key or os.environ.get('AZURE_OPENAI_KEY')
if not self.azure_endpoint or not self.api_key:
raise ValueError("Azure OpenAI endpoint and API key must be provided")
self.client = AzureOpenAI(
azure_endpoint=self.azure_endpoint,
api_key=self.api_key,
api_version=api_version
)
def is_available(self) -> bool:
try:
self.client.chat.completions.create(
model=self.deployment_name,
messages=[{"role": "user", "content": "test"}],
max_tokens=5
)
return True
except:
return False
def extract_parameters(self, request: str, param_schema: Dict[str, Any]) -> Dict[str, Any]:
if not self.is_available():
return {}
schema_text = "\n".join([
f"- {param}: {details.get('description', '')} (default: {details.get('default', 'None')})"
for param, details in param_schema.items()
])
instruction = """
Important specific rules for ARO parameters:
1. If request mentions "private", set "is_private" to true
2. Default "name" should be "agentic-aro" unless specified
3. Default region is "westus" unless specified
4. Default VM size is "Standard_D4s_v3" unless specified
5. Minimum worker_node_count is 3
"""
prompt = f"""
You are an AI assistant helping to extract parameters from a deployment request for an Azure Red Hat OpenShift (ARO) cluster.
REQUEST: "{request}"
Extract the following parameters:
{schema_text}
{instruction}
Format your response as a valid JSON object with the parameter names as keys.
ONLY include the extracted parameters in your response, nothing else - no explanation, no comments.
Return ONLY valid JSON.
If a parameter is not explicitly mentioned in the request, use the default value provided.
RESPONSE (JSON only):
"""
try:
response = self.client.chat.completions.create(
model=self.deployment_name,
messages=[
{"role": "system", "content": "You are a helpful assistant that extracts parameters from text and returns only JSON."},
{"role": "user", "content": prompt}
],
temperature=0.1,
max_tokens=1000
)
content = response.choices[0].message.content
json_match = re.search(r'\{[^}]+\}', content, re.DOTALL)
if json_match:
params = json.loads(json_match.group())
else:
params = json.loads(content)
self._apply_defaults(params, param_schema)
return params
except Exception as e:
if self.debug:
print(f"Error extracting parameters: {e}")
return {}
def _apply_defaults(self, params, param_schema):
for param, details in param_schema.items():
if param not in params or params[param] is None:
params[param] = details.get("default")
def extract_aro_parameters(self, request: str) -> Dict[str, Any]:
param_schema = {
"name": {"type": "string", "default": "agentic-aro", "description": "Cluster name"},
"region": {"type": "string", "default": "westus", "description": "Azure region"},
"worker_vm_size": {"type": "string", "default": "Standard_D4s_v3", "description": "VM size for workers"},
"worker_node_count": {"type": "integer", "default": 3, "description": "Number of worker nodes"},
"version": {"type": "string", "default": None, "description": "ARO version"},
"is_private": {"type": "boolean", "default": False, "description": "Private cluster"}
}
params = self.extract_parameters(request, param_schema)
if params.get("worker_node_count", 0) < 3:
params["worker_node_count"] = 3
return params
def extract_deletion_parameters(self, request: str) -> Dict[str, Any]:
param_schema = {
"name": {"type": "string", "default": None, "description": "Cluster name to delete"}
}
schema_text = "- name: Cluster name to delete (default: None)"
prompt = f"""
You are an AI assistant helping to extract parameters from a deletion request for an ARO cluster.
REQUEST: "{request}"
Extract the following parameters:
{schema_text}
The "name" should be the specific name of the ARO cluster to be deleted (e.g., "my-cluster", "test-deployment").
If no name is specified, use "agentic-aro" as default.
Format your response as a valid JSON object.
ONLY include the extracted parameters in your response, nothing else.
RESPONSE (JSON only):
"""
try:
response = self.client.chat.completions.create(
model=self.deployment_name,
messages=[
{"role": "system", "content": "You are a helpful assistant that extracts parameters from text and returns only JSON."},
{"role": "user", "content": prompt}
],
temperature=0.1,
max_tokens=500
)
content = response.choices[0].message.content
json_match = re.search(r'\{[^}]+\}', content, re.DOTALL)
if json_match:
params = json.loads(json_match.group())
else:
params = json.loads(content)
if not params.get("name"):
params["name"] = "agentic-aro"
return params
except Exception as e:
if self.debug:
print(f"Error extracting deletion parameters: {e}")
return {"name": "agentic-aro"}
Once this file is saved, we’re good to proceed onto the deployment specific portions of this task.