> ## Documentation Index
> Fetch the complete documentation index at: https://docs.formal.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Terraform

> Manage Formal infrastructure as code with the Terraform provider

## Overview

The [Formal Terraform Provider](https://registry.terraform.io/providers/formalco/formal/latest) enables you to manage Connectors, Resources, Policies, Users, and all other Formal objects as infrastructure-as-code.

## Installation

Add the provider to your Terraform configuration:

```hcl theme={null}
terraform {
  required_providers {
    formal = {
      source  = "formalco/formal"
      version = "~> 4.12.8"
    }
  }
}

provider "formal" {
  api_key = var.formal_api_key
}
```

## Authentication

Create an API key in the [Formal console](https://app.formal.ai/api-keys):

1. Navigate to [API Keys](https://app.formal.ai/api-keys)
2. Click **Create API Key**
3. Name the key (e.g., "terraform-production")
4. Copy the key immediately

Set the key as an environment variable or Terraform variable:

```bash theme={null}
export FORMAL_API_KEY="your-api-key-here"
```

Or in Terraform:

```hcl theme={null}
variable "formal_api_key" {
  description = "Formal API key"
  type        = string
  sensitive   = true
}
```

## Examples

### Full Production Stack

```hcl theme={null}
terraform {
  required_providers {
    formal = {
      source  = "formalco/formal"
      version = "~> 4.12.7"
    }
    aws = {
      source  = "hashicorp/aws"
      version = "~> 6.17.0"
    }
  }
}

# Variables
variable "name" {
  description = "Base name for resources"
  type        = string
  default     = "formal-production"
}

variable "datadog_application_key" {
  description = "Datadog Application Key"
  type        = string
  default     = "datadog-account-id"
  sensitive   = true
}

variable "datadog_api_key" {
  description = "Datadog API key"
  type        = string
  sensitive   = true
  default     = "datadog-api-key"
}

# Configure the Formal Provider
provider "formal" {
  api_key = "ENPxVb62qlm04BRU8Hkd7a5e31QzOW9X"
  retrieve_sensitive_values = false
}

# Configure AWS Provider
provider "aws" {
  region = "us-east-1"
}
# Create a Space
resource "formal_space" "production" {
  name = "production"
}

# Create a Resource
resource "formal_resource" "postgres" {
  name       = "production-postgres"
  technology = "postgres"
  hostname   = "prod-db.us-east-1.rds.amazonaws.com"
  port       = 5432
  space_id   = formal_space.production.id

  termination_protection = false
}

# Create a Connector
resource "formal_connector" "production" {
  name     = "production-connector"
  space_id = formal_space.production.id
}

# Configure Connector Settings
resource "formal_connector_configuration" "production" {
  connector_id = formal_connector.production.id
  log_level    = "info"
  # metrics collector endpoint. If there is no collector at the endpoint specified, the
  # connector will not send metrics
  otel_endpoint_hostname = "localhost"
  otel_endpoint_port     = 4317
}

# Create Connector Listener
resource "formal_connector_listener" "production_postgres" {
  connector_id = formal_connector.production.id
  name         = "production-postgres-listener"
  port         = 5432
}

# Add Listener Rule for Resource
resource "formal_connector_listener_rule" "postgres" {
  connector_listener_id = formal_connector_listener.production_postgres.id
  type                  = "resource"
  rule                  = formal_resource.postgres.id
}

# Create Users
resource "formal_user" "alice" {
  first_name = "Alice"
  last_name  = "Smith"
  email      = "alice@example.com"
  type       = "human"
}

resource "formal_user" "looker_app" {
  name = "Looker Application"
  type = "machine"
}

# Create Groups
resource "formal_group" "engineering" {
  name        = "Engineering Team"
  description = "Engineers with database access"
}

resource "formal_group_user_link" "alice_eng" {
  group_id = formal_group.engineering.id
  user_id  = formal_user.alice.id
}

# Create a Policy
resource "formal_policy" "mask_pii" {
  name         = "mask-pii-data"
  description  = "Mask PII fields for non-privileged users"
  status       = "active"
  owner        = "admin@example.com"
  notification = "none"

  module = <<-EOT
    package formal.v2

    import future.keywords.if
    import future.keywords.in

    response := {
      "action": "mask",
      "type": "nullify",
      "columns": pii_columns
    } if {
      not "pii_access" in input.user.groups

      pii_columns := [col |
        col := input.row[_]
        col["data_label"] in ["email", "ssn", "phone"]
      ]

      count(pii_columns) > 0
    }
  EOT
}

# AWS S3 Bucket for Logs
resource "aws_s3_bucket" "logs" {
  bucket        = "${var.name}-logs"
  force_destroy = true
}

# AWS Integration
resource "formal_integration_cloud" "aws" {
  name         = "aws-production"
  cloud_region = "us-east-1"

  aws {
    template_version          = "latest"
    enable_rds_autodiscovery  = true
    enable_eks_autodiscovery  = true
    allow_s3_access           = true
    s3_bucket_arn             = "${aws_s3_bucket.logs.arn}/*"
  }
}

# Log Integration
resource "formal_integration_log" "datadog" {
  name = "datadog-logs"

  datadog {
    account_id = var.datadog_application_key
    api_key    = var.datadog_api_key
    site       = "datadoghq.com"
  }
}

```

## Resource Documentation

Full documentation for all resources:

**Core Resources:**

* [formal\_connector](https://registry.terraform.io/providers/formalco/formal/latest/docs/resources/connector)
* [formal\_resource](https://registry.terraform.io/providers/formalco/formal/latest/docs/resources/resource)
* [formal\_user](https://registry.terraform.io/providers/formalco/formal/latest/docs/resources/user)
* [formal\_group](https://registry.terraform.io/providers/formalco/formal/latest/docs/resources/group)
* [formal\_policy](https://registry.terraform.io/providers/formalco/formal/latest/docs/resources/policy)
* [formal\_space](https://registry.terraform.io/providers/formalco/formal/latest/docs/resources/space)
* [formal\_satellite](https://registry.terraform.io/providers/formalco/formal/latest/docs/resources/satellite)
* [formal\_workflow](https://registry.terraform.io/providers/formalco/formal/latest/docs/resources/workflow)
* [formal\_form](https://registry.terraform.io/providers/formalco/formal/latest/docs/resources/form)

**Integrations:**

* [formal\_integration\_cloud](https://registry.terraform.io/providers/formalco/formal/latest/docs/resources/integration_cloud)
* [formal\_integration\_log](https://registry.terraform.io/providers/formalco/formal/latest/docs/resources/integration_log)
* [formal\_integration\_bi](https://registry.terraform.io/providers/formalco/formal/latest/docs/resources/integration_bi)

**Data Sources:**

* [formal\_connector](https://registry.terraform.io/providers/formalco/formal/latest/docs/data-sources/connector) - Look up a connector by ID or name
* [formal\_group](https://registry.terraform.io/providers/formalco/formal/latest/docs/data-sources/group) - Look up a group by ID or name
* [formal\_resource](https://registry.terraform.io/providers/formalco/formal/latest/docs/data-sources/resource) - Look up a resource by ID or name
* [formal\_space](https://registry.terraform.io/providers/formalco/formal/latest/docs/data-sources/space) - Look up a space by ID or name

## Example Repositories

Formal provides complete Terraform examples:

* [AWS Connector Deployment](https://github.com/formalco/terraform-provider-formal/tree/main/examples/deployments/aws)
* [Policy Example](https://github.com/formalco/terraform-provider-formal/tree/main/examples/policies)

Clone and customize for your needs:

```bash theme={null}
git clone https://github.com/formalco/terraform-provider-formal.git
cd terraform-provider-formal/examples/deployments/aws/connector
```

## Best Practices

<AccordionGroup>
  <Accordion title="Use Version Constraints" icon="lock">
    Pin provider versions to avoid unexpected changes:

    ```hcl theme={null}
    formal = {
      source  = "formalco/formal"
      version = "~> 4.12.8"  # Allow patches, not minor versions
    }
    ```
  </Accordion>

  <Accordion title="Store State Remotely" icon="cloud">
    Use remote state backends (S3, Terraform Cloud) for team collaboration:

    ```hcl theme={null}
    terraform {
      backend "s3" {
        bucket = "my-terraform-state"
        key    = "formal/production/terraform.tfstate"
        region = "us-east-1"
      }
    }
    ```
  </Accordion>

  <Accordion title="Use Variables" icon="input-text">
    Parameterize configurations for reusability:

    ```hcl theme={null}
    variable "environment" {
      type = string
    }

    resource "formal_connector" "main" {
      name = "${var.environment}-connector"
    }
    ```
  </Accordion>

  <Accordion title="Separate Environments" icon="layer-group">
    Use workspaces or separate state files for prod/staging/dev:

    ```bash theme={null}
    terraform workspace new production
    terraform workspace select production
    ```
  </Accordion>

  <Accordion title="Enable Termination Protection" icon="shield">
    Protect production resources:

    ```hcl theme={null}
    resource "formal_resource" "prod_db" {
      name                   = "production-postgres"
      termination_protection = true
      # ...
    }
    ```
  </Accordion>
</AccordionGroup>

## Importing Existing Resources

Bring existing Formal objects under Terraform management:

```bash theme={null}
# Import a Resource
terraform import formal_resource.postgres <resource-id>

# Import a Connector
terraform import formal_connector.main <connector-id>

# Import a User
terraform import formal_user.alice <user-id>
```

Get object IDs from the Formal console or API.

## Outputs

Export useful information:

```hcl theme={null}
output "connector_token" {
    description = "Connector API token for deployment"
    value       = formal_connector.production.api_key
    sensitive   = true
  }

  output "connector_listener_port" {
    description = "Configured listener port"
    value       = formal_connector_listener.production_postgres.port
  }

  output "resource_ids" {
    description = "Map of resource names to IDs"
    value = {
      (formal_space.production.name)                       = formal_space.production.id
      (formal_resource.postgres.name)                      = formal_resource.postgres.id
      (formal_connector.production.name)                   = formal_connector.production.id
      (formal_connector_listener.production_postgres.name) = formal_connector_listener.production_postgres.id
      (formal_group.engineering.name)                      = formal_group.engineering.id
    }
  }
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Provider Docs" icon="book" href="https://registry.terraform.io/providers/formalco/formal/latest/docs">
    Complete Terraform provider documentation
  </Card>

  <Card title="Examples" icon="code" href="https://github.com/formalco/terraform-provider-formal">
    Deployment examples on GitHub
  </Card>
</CardGroup>
