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.
Overview
The Formal Terraform Provider 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:
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:
- Navigate to API Keys
- Click Create API Key
- Name the key (e.g., “terraform-production”)
- Copy the key immediately
Set the key as an environment variable or Terraform variable:
export FORMAL_API_KEY="your-api-key-here"
Or in Terraform:
variable "formal_api_key" {
description = "Formal API key"
type = string
sensitive = true
}
Examples
Full Production Stack
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
# 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:
Integrations:
Data Sources:
Example Repositories
Formal provides complete Terraform examples:
Clone and customize for your needs:
git clone https://github.com/formalco/terraform-provider-formal.git
cd terraform-provider-formal/examples/deployments/aws/connector
Best Practices
Pin provider versions to avoid unexpected changes:formal = {
source = "formalco/formal"
version = "~> 4.12.8" # Allow patches, not minor versions
}
Use remote state backends (S3, Terraform Cloud) for team collaboration:terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "formal/production/terraform.tfstate"
region = "us-east-1"
}
}
Parameterize configurations for reusability:variable "environment" {
type = string
}
resource "formal_connector" "main" {
name = "${var.environment}-connector"
}
Use workspaces or separate state files for prod/staging/dev:terraform workspace new production
terraform workspace select production
Enable Termination Protection
Protect production resources:resource "formal_resource" "prod_db" {
name = "production-postgres"
termination_protection = true
# ...
}
Importing Existing Resources
Bring existing Formal objects under Terraform management:
# 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:
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
Provider Docs
Complete Terraform provider documentation
Examples
Deployment examples on GitHub