Skip to main content
Glama

MCP SysOperator

by tarnover
terraform.ts6.24 kB
import { AnsibleExecutionError } from '../common/errors.js'; import { execAsync, createTempDirectory, writeTempFile, cleanupTempDirectory, verifyTerraformInstalled, verifyTflocalInstalled } from '../common/utils.js'; import { TerraformOptions } from '../common/types.js'; // Export schema for use in index.ts export { TerraformSchema } from '../common/types.js'; /** * Helper function to format command-line parameters from a map of key-value pairs * @param params Record containing parameters * @param prefix The prefix to add before each parameter (e.g., -var, -var-file) * @returns Formatted parameter string */ function formatCommandParams(params: Record<string, any> | undefined, prefix: string): string { if (!params || Object.keys(params).length === 0) { return ''; } return Object.entries(params) .map(([key, value]) => { if (typeof value === 'string') { // For string values, wrap in quotes return `${prefix} ${key}="${value}"`; } else if (typeof value === 'object') { // For objects/arrays, convert to JSON and wrap in quotes return `${prefix} ${key}='${JSON.stringify(value)}'`; } else { // For numbers, booleans, etc. return `${prefix} ${key}=${value}`; } }) .join(' '); } /** * Execute Terraform commands * @param options Terraform options * @returns Result of Terraform operation */ export async function terraformOperations(options: TerraformOptions): Promise<string> { const { action, workingDir, varFiles, vars, useLocalstack, autoApprove, backendConfig, state, target, lockTimeout, refresh, workspace } = options; // Determine if we should use terraform or tflocal command const terraformCmd = useLocalstack ? 'tflocal' : 'terraform'; // If using tflocal, verify it's installed if (useLocalstack) { await verifyTflocalInstalled(); } else { // Otherwise, verify terraform is installed await verifyTerraformInstalled(); } // Base command with terraform/tflocal and action let command = `cd "${workingDir}" && ${terraformCmd} ${action}`; // Add var files if specified if (varFiles && varFiles.length > 0) { command += ' ' + varFiles.map(file => `-var-file="${file}"`).join(' '); } // Add vars if specified if (vars && Object.keys(vars).length > 0) { command += ' ' + formatCommandParams(vars, '-var'); } // Add backend config if specified if (backendConfig && Object.keys(backendConfig).length > 0) { command += ' ' + formatCommandParams(backendConfig, '-backend-config'); } // Add specific parameters based on action switch (action) { case 'init': // No special options needed here break; case 'apply': case 'destroy': // Add auto-approve if specified if (autoApprove) { command += ' -auto-approve'; } // Add refresh option if (refresh !== undefined) { command += ` -refresh=${refresh ? 'true' : 'false'}`; } // Add state file if specified if (state) { command += ` -state="${state}"`; } // Add targets if specified if (target && target.length > 0) { command += ' ' + target.map(t => `-target="${t}"`).join(' '); } // Add lock timeout if specified if (lockTimeout) { command += ` -lock-timeout=${lockTimeout}`; } break; case 'plan': // Add refresh option if (refresh !== undefined) { command += ` -refresh=${refresh ? 'true' : 'false'}`; } // Add state file if specified if (state) { command += ` -state="${state}"`; } // Add targets if specified if (target && target.length > 0) { command += ' ' + target.map(t => `-target="${t}"`).join(' '); } // Add lock timeout if specified if (lockTimeout) { command += ` -lock-timeout=${lockTimeout}`; } break; case 'workspace': // Add workspace name if specified if (workspace) { command += ` select ${workspace}`; } else { command += ' list'; // Default to listing workspaces if no name is provided } break; // For other actions (validate, output, import), no special handling needed } // For debug purposes console.log(`Executing Terraform command: ${command}`); try { // Execute the command const { stdout, stderr } = await execAsync(command); // Adjust output based on action switch (action) { case 'output': // Try to parse JSON output try { const outputJson = JSON.parse(stdout); return JSON.stringify(outputJson, null, 2); } catch (error) { // If not JSON, return as is return stdout; } default: return stdout || `Terraform ${action} completed successfully (no output).`; } } catch (error: any) { const errorMessage = error.stderr || error.message || 'Unknown error'; throw new AnsibleExecutionError(`Terraform execution failed for ${action}: ${errorMessage}`, error.stderr); } } /** * Execute Terraform with a temporary tfvars file * @param options Terraform options * @param tfvarsContent Content for a temporary .tfvars file * @returns Result of Terraform operation */ export async function terraformWithTfvars(options: TerraformOptions, tfvarsContent: string): Promise<string> { let tempDir: string | undefined; try { // Create a temp directory for the tfvars file tempDir = await createTempDirectory('terraform-tfvars'); // Write tfvars content to a file const tfvarsPath = await writeTempFile(tempDir, 'terraform.tfvars', tfvarsContent); // Add this file to varFiles array const varFiles = options.varFiles ? [...options.varFiles, tfvarsPath] : [tfvarsPath]; // Call terraformOperations with updated options return await terraformOperations({ ...options, varFiles }); } finally { // Cleanup temp directory if (tempDir) { await cleanupTempDirectory(tempDir); } } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/tarnover/mcp-sysoperator'

If you have feedback or need assistance with the MCP directory API, please join our Discord server