# Terraform

> Keep agents, their tools and credentials, their evaluations and your webhook receivers in Terraform.

The Terraform provider manages what [Agents as code](/guides/agents-as-code),
[Tools and credentials](/guides/tools) and [Evaluations in your pipeline](/guides/evaluations) do
through the API, so one `terraform apply` can put a customer's whole setup in place and the next one
shows what changed in the console since.

| Resource | What it is |
|---|---|
| `aigently_agent` | An agent, kept as its definition, and published if you ask |
| `aigently_tool` | A request an agent can make mid-conversation |
| `aigently_mcp_server` | A remote MCP server whose tools agents can be given |
| `aigently_vault_secret` | A tool credential, written and never read back |
| `aigently_evaluation` | A conversation an agent has to keep handling |
| `aigently_webhook_endpoint` | A receiver for a project's events ([Webhooks](/guides/webhooks)) |

Two data sources read what you do not manage: `aigently_project`, found by its name, and
`aigently_agent`, by its id.

## Getting it

The provider will be published to the Terraform Registry, as `aigently/aigently`, with our other
SDKs. Until then, every resource here is a request you can make yourself, and the guides linked above
show each one.

## The key it needs

A **live** key ([Keys](/guides/keys)), in `AIGENTLY_API_KEY` or the provider's `api_key`, holding what
your configuration uses: `agents:read` and `agents:write` for agents, `tools:write` for tools and MCP
servers, `vault:write` for credentials — which only an owner can give a key — `evaluations:run` with
`agents:write` for evaluations, and `webhooks:read` and `webhooks:write` for receivers.

## A configuration

```hcl
terraform {
  required_providers {
    aigently = { source = "aigently/aigently" }
  }
}

provider "aigently" {}

variable "orders_api_token" {
  type      = string
  sensitive = true
}

data "aigently_project" "front" {
  name = "Front desk"
}

resource "aigently_vault_secret" "orders" {
  name              = "Orders API"
  secret_wo         = var.orders_api_token
  secret_wo_version = 1
  project_ids       = [data.aigently_project.front.id]
}

resource "aigently_tool" "lookup_order" {
  project_id  = data.aigently_project.front.id
  name        = "lookup_order"
  description = "Look up the status of an order by its number."
  parameters  = jsonencode({ type = "object", properties = { order_id = { type = "string" } } })
  http = jsonencode({
    method = "GET"
    url    = "https://orders.aigently.ai/v1/orders/{order_id}"
    auth   = { type = "bearer", vault_key_id = aigently_vault_secret.orders.id }
  })
}

resource "aigently_agent" "front_desk" {
  project_id = data.aigently_project.front.id
  definition = jsonencode(merge(
    jsondecode(file("front-desk.json")),
    { references = { tools = [aigently_tool.lookup_order.name] } },
  ))
  publish = true
}
```

`front-desk.json` is the file the console exports from the agent's menu, or what
`GET /v1/agents/{id}/definition` answers.

## How a change shows in a plan

- **A definition is compared for what it says.** The API fills in every default a definition leaves
  out, and none of those is a change. A value edited in the console is: the plan shows that one value
  going back to what you wrote.
- **Name a tool through its resource**, as above, so Terraform makes the tool before the agent. A
  definition that names something the project does not have is applied without it, with a warning,
  and the plan keeps showing it until the project has it.
- **A definition for another kind or direction of agent makes a new agent**, because an agent keeps
  both for its whole life.
- **Header values and an MCP server's address are hidden when they are read**, and the provider keeps
  what you configured rather than the hidden form, so they show no change.

## Publishing

With `publish = true`, every change is published through the console's own Publish check, and
`published_version` is the version it made — what [Pin a version](/guides/variables#pin-a-version)
pins a conversation to. A refused publish fails the apply with every reason. On an existing agent,
the next apply publishes again; a new agent stays made, and Terraform marks it tainted, so run
`terraform untaint` once the reasons are fixed to keep it.

## Credentials

`secret_wo` sends a credential's value without Terraform ever keeping it, and sends it again when you
raise `secret_wo_version`. It needs Terraform 1.11 or later. `secret` works with any version, and
Terraform keeps it in its state, as it does every sensitive value. Either way, a value replaced in the
console is noticed from its last four characters, and the next apply puts yours back.

## Importing

Each resource imports by its id: `terraform import aigently_agent.front_desk 8a1f3c2e-…`. An
evaluation imports as `<agent_id>/<evaluation_id>`. Values a read hides — a tool's headers, a
credential, a receiver's signing secret — are not imported. The first plan after an import shows a
tool's hidden values being set, and a receiver's secret stays empty until it is made again.
