# Terraform # Terraform Overview Monad publishes an official [Terraform](https://developer.hashicorp.com/terraform) provider, so you can manage your pipelines and their components as code. You declare inputs, transforms, enrichments, outputs, secrets, pipelines and alert rules in `.tf` files, review changes as a `terraform plan`, and apply them the same way you ship any other infrastructure. The provider is published on the Terraform Registry as [`monad-inc/monad`](https://registry.terraform.io/providers/monad-inc/monad/latest), and its source is on [GitHub](https://github.com/monad-inc/terraform-provider-monad). This section covers what the provider manages, how to configure it, and a first pipeline. For the practices that keep a Terraform-managed organization healthy, see **[Best Practices](/terraform/best-practices)**. For what the provider does not cover yet, see **[Limitations](/terraform/limitations)**. ## What the Provider Manages | Resource | Manages | |---|---| | [`monad_input`](https://registry.terraform.io/providers/monad-inc/monad/latest/docs/resources/input) | A source connector that pulls or receives records | | [`monad_transform`](https://registry.terraform.io/providers/monad-inc/monad/latest/docs/resources/transform) | An ordered list of operations applied to each record | | [`monad_enrichment`](https://registry.terraform.io/providers/monad-inc/monad/latest/docs/resources/enrichment) | A lookup that adds context to each record | | [`monad_output`](https://registry.terraform.io/providers/monad-inc/monad/latest/docs/resources/output) | A destination connector | | [`monad_pipeline`](https://registry.terraform.io/providers/monad-inc/monad/latest/docs/resources/pipeline) | The graph of nodes and conditional edges that connects the components | | [`monad_secret`](https://registry.terraform.io/providers/monad-inc/monad/latest/docs/resources/secret) | A credential that components reference by ID | | [`monad_alert_rule`](https://registry.terraform.io/providers/monad-inc/monad/latest/docs/resources/alert_rule) | A rule that watches pipelines and raises alerts | Each resource page on the registry is the full reference: every argument, nested block, import syntax and worked examples. This page does not repeat it. ## Requirements - **Terraform 1.11 or later.** Secret values are declared as [write-only arguments](https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments): they are sent to Monad but never stored in Terraform state. Earlier Terraform releases reject the provider's schema. - **An organization API key** with read and write permissions on the resource types you manage. Create one under **Settings → API Keys**; see [API Keys](/guides/api-keys). - **Your organization ID**, shown in the Monad UI under **Settings**. ## Configure the Provider ```hcl terraform { required_version = ">= 1.11" required_providers { monad = { source = "monad-inc/monad" version = "~> 0.5.0" } } } provider "monad" { base_url = "https://app.monad.com" # the default; set your own host if self-hosted api_token = var.monad_api_token # or MONAD_API_TOKEN organization_id = var.monad_org_id # or MONAD_ORGANIZATION_ID } variable "monad_api_token" { type = string sensitive = true } variable "monad_org_id" { type = string } ``` `base_url` defaults to `https://app.monad.com`, the Monad SaaS platform. For a self-hosted deployment, set it to your own host. Give the host only: the provider appends `/api` itself. Every provider argument can come from an environment variable instead, which keeps the API key out of your configuration files: | Argument | Environment variable | |---|---| | `api_token` | `MONAD_API_TOKEN` | | `organization_id` | `MONAD_ORGANIZATION_ID` | | `base_url` | `MONAD_BASE_URL` | | `request_timeout` | `MONAD_REQUEST_TIMEOUT` | An argument set in the provider block takes precedence over its environment variable. ## Your First Pipeline This configuration creates a secret, an S3 output that uses it, an HTTP input, and a pipeline that connects the two. The pipeline is created **disabled**, so nothing flows until you have checked it. ```hcl variable "s3_access_key" { type = string sensitive = true } variable "s3_secret_key" { type = string sensitive = true } resource "monad_secret" "s3_access_key" { name = "s3-access-key" value = var.s3_access_key # write-only: sent to Monad, never kept in state } resource "monad_secret" "s3_secret_key" { name = "s3-secret-key" value = var.s3_secret_key } resource "monad_input" "events" { name = "Application events" type = "monad-http" } resource "monad_output" "archive" { name = "S3 archive" type = "s3" config { settings = { bucket = "acme-security-archive" region = "us-west-2" prefix = "monad/events" compression = "none" partition_format = "simple date" format_config = { Format = "json" json_format = { type = "line" } } } secrets = { access_key = { id = monad_secret.s3_access_key.id } secret_key = { id = monad_secret.s3_secret_key.id } } } } resource "monad_pipeline" "events" { name = "Application events to S3" enabled = false nodes { slug = "events" component_type = "input" component_id = monad_input.events.id } nodes { slug = "archive" component_type = "output" component_id = monad_output.archive.id } edges { from_node_instance_slug = "events" to_node_instance_slug = "archive" condition { operator = "always" } } } ``` A few things to notice: - **Components come first, the pipeline references them.** Because `monad_pipeline` refers to `monad_input.events.id` and `monad_output.archive.id`, Terraform creates the components before the pipeline and destroys the pipeline before the components. - **Connector fields are the connector's API field names.** `config.settings` and `config.secrets` take the same names as the connector's JSON configuration. Each connector's page in these docs ([Inputs](/inputs/index), [Outputs](/outputs/index), [Enrichments](/enrichments/index)) lists its settings and secrets, and its **API Examples** section shows the JSON to mirror in HCL. - **Credentials are always references.** A secret slot takes `{ id = monad_secret..id }`, never a raw string. See [Secrets](/guides/secrets) for how secrets work in Monad. - **Edges connect nodes by slug.** Set `slug` on every node so edges can name it, and give every edge a condition; `operator = "always"` passes every record. [Conditionals](/conditionals) describes the routing rules you can use instead. Supply the two key values as `TF_VAR_s3_access_key` and `TF_VAR_s3_secret_key` (or in a `.tfvars` file you keep out of version control). Run `terraform init`, then `terraform plan` to review, then `terraform apply`. When the pipeline looks right in the Monad UI, change `enabled` to `true` and apply again. ## Managing an Existing Organization Every resource supports `terraform import` and Terraform `import` blocks by ID, so you can adopt components and pipelines that were built in the UI without recreating them. The registry page for each resource shows the syntax. Because the provider has no data sources, it cannot list what already exists in your organization. The open-source [`monad-org-export`](https://github.com/monad-inc/community/tree/main/scripts/monad-org-export) script fills that gap: it reads an organization over the Monad API and writes a complete Terraform module for it. Run it with `--emit-imports` to bring an existing organization under Terraform in place, or without it to copy an organization to another organization or instance. Its README documents the flags and caveats. ## Getting Help The [changelog](https://github.com/monad-inc/terraform-provider-monad/blob/main/CHANGELOG.md) lists every release and the migration path for each breaking change. If a plan or apply behaves in a way these pages do not explain, contact Monad support with the resource type, the provider version (`terraform version` prints it), and the error text.