Agent DevOps Pattern
This document describes the reusable pattern for agent-driven infrastructure operations.
Overview
Infrastructure operations in ClearlyOps follow a consistent pattern that is safe for AI agents to execute:
- Config-driven: one TOML file per subsystem
- Idempotent: safe to run multiple times
- Plan/Apply: preview changes before applying
- Auditable: JSON receipts for every operation
Contract
Every agent-facing infra tool follows this interface:
<tool> plan [--config PATH] [--format json|text]
<tool> apply [--config PATH] --confirm RESOURCE [--format json|text]
<tool> status [--config PATH] [--format json|text]
Commands
| Command | Description | Mutates? |
|---|---|---|
plan |
Show what would change (dry-run) | No |
apply |
Apply changes with confirmation | Yes |
status |
Show current resource state | No |
Safety
- Default is read-only:
planandstatusare the default - Explicit confirmation:
applyrequires--confirm <resource>to prevent accidental mutations - No secrets in output: tokens are redacted; never written to disk or logs
Output
All tools emit structured output:
{
"timestamp": "2026-01-17T18:30:00Z",
"command": "apply",
"config_path": "deploy/infra/docs.toml",
"changes": [
{
"resource_type": "pages_project",
"resource_id": "abc123",
"action": "create",
"details": {"name": "clearlyops-docs-public"}
}
],
"errors": []
}
Use --format json for machine-readable output.
Directory Structure
deploy/
infra/
docs.toml # Docs hosting config
docs.example.toml # Example with placeholders
api.toml # (future) API deployment config
...
scripts/
setup_docs_infra.py # Docs infra tool
ops/ # (future) Additional ops tools
api_deploy.py
...
Secrets
Secrets are never stored in the repo or in GitHub Secrets:
- Source of truth: GCP Secret Manager
- CI access: GitHub OIDC → GCP Workload Identity → Secret Manager
- Local access:
gcloud secrets versions access ...
Tools fetch secrets at runtime and redact them from all output.
Example: Docs Infrastructure
Config (deploy/infra/docs.toml)
[gcp]
project_id = "clearlyops-prod"
secret_cloudflare_api_token = "cloudflare-pages-api-token"
[cloudflare]
account_id = "abc123"
zone_id = "def456"
[docs.public]
pages_project_name = "clearlyops-docs-public"
custom_domain = "docs.clearlyops.com"
[docs.internal]
pages_project_name = "clearlyops-docs-internal"
custom_domain = "docs-internal.clearlyops.com"
preview_deployments = "disabled"
[access]
allowed_emails = ["[email protected]"]
Usage
# Preview changes
python scripts/setup_docs_infra.py plan
# Apply (requires confirmation)
python scripts/setup_docs_infra.py apply --confirm docs-internal.clearlyops.com
# Get JSON receipt
python scripts/setup_docs_infra.py status --format json
Extending the Pattern
To add a new infrastructure tool:
- Create config:
deploy/infra/<subsystem>.toml - Create tool:
scripts/setup_<subsystem>_infra.pyorscripts/ops/<subsystem>.py - Implement commands:
plan,apply,status - Ensure idempotency: check if resources exist before creating
- Emit receipts: use the standard JSON receipt format
- Fetch secrets from GCP: use
gcloud secrets versions access ...
Receipt Format
@dataclass
class ResourceChange:
resource_type: str # e.g., "pages_project", "dns_record"
resource_id: str # Unique identifier
action: str # "create", "update", "no_change"
details: dict # Resource-specific details
@dataclass
class InfraReceipt:
timestamp: str # ISO 8601
command: str # "plan", "apply", "status"
config_path: str # Path to config file
changes: list[ResourceChange]
errors: list[str] # Error messages (if any)
Agent Workflow
Agents should follow this workflow:
- Read config: load
deploy/infra/<subsystem>.toml - Plan first: always run
planbeforeapply - Review changes: check the receipt for expected changes
- Apply with confirmation: run
apply --confirm <resource> - Verify: run
statusto confirm state - Store receipt: save JSON receipt for audit trail
Example Agent Prompt
You are managing infrastructure for ClearlyOps.
To update docs hosting:
1. Run: python scripts/setup_docs_infra.py plan
2. Review the output for expected changes
3. If changes look correct, run: python scripts/setup_docs_infra.py apply --confirm docs-internal.clearlyops.com
4. Verify with: python scripts/setup_docs_infra.py status --format json
Future Tools
Planned agent-facing infra tools:
| Tool | Config | Purpose |
|---|---|---|
setup_docs_infra.py |
docs.toml |
Docs hosting (Cloudflare Pages + Access) |
setup_api_infra.py |
api.toml |
API deployment (Cloud Run) |
setup_db_infra.py |
db.toml |
Database provisioning (Cloud SQL) |
setup_dns_infra.py |
dns.toml |
DNS records |
All follow the same plan/apply/status + JSON receipt pattern.
Comparison: Script vs Terraform
| Aspect | Script | Terraform |
|---|---|---|
| Setup | Minimal (Python only) | Requires state backend |
| Learning curve | Low | Medium |
| Drift detection | Manual (via status) |
Automatic |
| State management | Stateless (idempotent checks) | Stateful |
| Agent compatibility | Excellent (simple CLI) | Good (after setup) |
| Best for | Early stage, fast iteration | Multi-env, complex infra |
Current approach: scripts with Terraform-like semantics (plan/apply). If infra complexity grows, migrate to Terraform while keeping the same agent-facing interface.