Special Thanks Shoutout to Jason Hernandez and the Materialize team for creating the initial version of this provider! Also see NOTICE.md
This project is licensed under the Apache License, Version 2.0. See the LICENSE file for details.
A Terraform provider for managing Ory Network resources using infrastructure-as-code.
Note: This provider is for Ory Network (the managed SaaS offering) only. It does not support self-hosted Ory deployments.
- Project & Workspace Management: Create and manage Ory Network projects and workspaces
- Project Configuration: CORS, session settings, password policies, MFA, OAuth2/Hydra settings
- Identity Management: Create and manage user identities with custom schemas
- Identity Schemas: Define custom identity schemas for your project
- Authentication Flows: Configure social providers (Google, GitHub, Microsoft, Apple, OIDC)
- Webhooks/Actions: Trigger webhooks on identity flow events
- Email Templates: Customize verification, recovery, and login code emails
- OAuth2 Clients: Manage OAuth2/OIDC client applications and dynamic client registration (RFC 7591)
- JSON Web Key Sets: Manage JWK sets for token signing and verification
- JWT Grant Trust: Trust external identity providers for RFC 7523 JWT Bearer grants
- Event Streams: Publish Ory events to external systems like AWS SNS (Enterprise)
- Organizations: Multi-tenancy support for B2B applications
- Permissions (Keto): Manage relationship tuples for fine-grained authorization
- API Key Management: Manage project API keys
- Project / Workspace — Look up existing projects and workspaces
- Identity — Query user identities
- Identity Schemas — List available identity schemas
- OAuth2 Client — Look up OAuth2 client details
- Organization — Look up organization details
- Terraform >= 1.0
- Go >= 1.21 (for building from source)
- An Ory Network account
terraform {
required_providers {
ory = {
source = "ory/ory"
}
}
}git clone https://github.com/ory/terraform-provider-ory.git
cd terraform-provider-ory
make buildThen configure Terraform to use the local provider:
# ~/.terraformrc
provider_installation {
dev_overrides {
"ory/ory" = "/path/to/terraform-provider-ory"
}
direct {}
}Ory Network uses two types of API keys:
| Key Type | Prefix | Purpose |
|---|---|---|
| Workspace API Key | ory_wak_... |
Projects, organizations, workspace management |
| Project API Key | ory_pat_... |
Identities, OAuth2 clients, relationships |
export ORY_WORKSPACE_API_KEY="ory_wak_..."
export ORY_PROJECT_API_KEY="ory_pat_..."
export ORY_PROJECT_ID="your-project-uuid"
export ORY_PROJECT_SLUG="your-project-slug" # e.g., "vibrant-moore-abc123"provider "ory" {
workspace_api_key = var.ory_workspace_key # or ORY_WORKSPACE_API_KEY env var
project_api_key = var.ory_project_key # or ORY_PROJECT_API_KEY env var
project_id = var.ory_project_id # or ORY_PROJECT_ID env var
project_slug = var.ory_project_slug # or ORY_PROJECT_SLUG env var
}terraform {
required_providers {
ory = {
source = "ory/ory"
}
}
}
provider "ory" {}
# Configure project settings
resource "ory_project_config" "main" {
cors_enabled = true
cors_origins = ["https://app.example.com"]
password_min_length = 10
session_lifespan = "720h" # 30 days
}
# Add Google social login
resource "ory_social_provider" "google" {
provider_id = "google"
client_id = var.google_client_id
client_secret = var.google_client_secret
scope = ["email", "profile"]
}
# Create a webhook for new registrations
resource "ory_action" "welcome_email" {
flow = "registration"
timing = "after"
auth_method = "password"
url = "https://api.example.com/webhooks/welcome"
method = "POST"
}# Create organizations for each tenant
resource "ory_organization" "acme" {
label = "Acme Corporation"
domains = ["acme.com"]
}
resource "ory_organization" "globex" {
label = "Globex Inc"
domains = ["globex.com"]
}
# Identity schema with organization support
resource "ory_identity_schema" "customer" {
name = "customer_v1"
schema = jsonencode({
"$id" = "https://example.com/customer.schema.json"
"$schema" = "http://json-schema.org/draft-07/schema#"
title = "Customer"
type = "object"
properties = {
traits = {
type = "object"
properties = {
email = {
type = "string"
format = "email"
"ory.sh/kratos" = {
credentials = { password = { identifier = true } }
verification = { via = "email" }
recovery = { via = "email" }
}
}
name = {
type = "object"
properties = {
first = { type = "string" }
last = { type = "string" }
}
}
}
required = ["email"]
}
}
})
}resource "ory_oauth2_client" "api_service" {
client_name = "API Service"
grant_types = ["client_credentials"]
token_endpoint_auth_method = "client_secret_post"
scope = "read write"
}
output "client_id" {
value = ory_oauth2_client.api_service.client_id
}
output "client_secret" {
value = ory_oauth2_client.api_service.client_secret
sensitive = true
}resource "ory_project_config" "secure" {
# Enable TOTP (authenticator apps)
enable_totp = true
totp_issuer = "MyApp"
# Enable WebAuthn (security keys, passkeys)
enable_webauthn = true
webauthn_rp_display_name = "MyApp"
webauthn_rp_id = "app.example.com"
webauthn_rp_origins = ["https://app.example.com"]
webauthn_passwordless = true
# Require MFA for all users
required_aal = "aal2"
}resource "ory_email_template" "recovery" {
template_type = "recovery_code_valid"
subject = "Reset your password"
body_html = <<-HTML
<h1>Password Reset</h1>
<p>Hi {{ .Identity.traits.name.first }},</p>
<p>Your recovery code is: <strong>{{ .RecoveryCode }}</strong></p>
HTML
body_plaintext = <<-TEXT
Password Reset
Hi {{ .Identity.traits.name.first }},
Your recovery code is: {{ .RecoveryCode }}
TEXT
}git clone https://github.com/ory/terraform-provider-ory.git
cd terraform-provider-ory
# Install dependencies and development tools (linters, doc generators, security scanners)
make deps
# Set up git hooks (conventional commit validation, pre-push checks)
git config core.hooksPath .githooks
# Build the provider
make build
# Install to local Terraform plugins directory
make installUnit tests run without any credentials:
make test # Run all unit tests
make test-short # Run unit tests in short modeAcceptance tests run against a pre-created Ory project. Copy .env.example to .env and fill in your credentials:
cp .env.example .envAt minimum you need:
# Workspace credentials
ORY_WORKSPACE_API_KEY=ory_wak_...
ORY_WORKSPACE_ID=...
# Pre-created test project
ORY_PROJECT_ID=...
ORY_PROJECT_SLUG=...
ORY_PROJECT_API_KEY=ory_pat_...
ORY_PROJECT_ENVIRONMENT=prodThe .env file is gitignored and automatically loaded by make targets.
make test-acc # Standard acceptance tests
make test-acc-verbose # With debug logging
make test-acc-keto # Run only Keto/relationship tests
make test-acc-all # All tests with all features enabledOr run directly with go test:
# Acceptance tests
TF_ACC=1 go test -tags acceptance -p 1 -v -timeout 30m ./...
# Specific resource tests
TF_ACC=1 go test -tags acceptance -p 1 -v ./internal/resources/identity/...Some tests require additional feature flags or specific Ory plan features:
| Environment Variable | Purpose | Default |
|---|---|---|
TF_ACC=1 |
Enable acceptance tests | Required |
ORY_KETO_TESTS_ENABLED=true |
Run Relationship tests (requires Keto) | Skipped |
ORY_B2B_ENABLED=true |
Run Organization tests (requires B2B plan) | Skipped |
ORY_SOCIAL_PROVIDER_TESTS_ENABLED=true |
Run social provider tests | Skipped |
ORY_SCHEMA_TESTS_ENABLED=true |
Run IdentitySchema tests (schemas can't be deleted) | Skipped |
ORY_PROJECT_TESTS_ENABLED=true |
Run Project create/delete tests | Skipped |
ORY_EVENT_STREAM_TESTS_ENABLED=true |
Run Event Stream tests (requires Enterprise plan) | Skipped |
| Test Suite | Free Plan | Growth Plan | Enterprise |
|---|---|---|---|
| Identity | ✅ | ✅ | ✅ |
| OAuth2 Client | ✅ | ✅ | ✅ |
| OIDC Dynamic Client | ✅ | ✅ | ✅ |
| Project Config | ✅ | ✅ | ✅ |
| Action (webhooks) | ✅ | ✅ | ✅ |
| Email Template | ✅ | ✅ | ✅ |
| Social Provider | ✅ | ✅ | ✅ |
| JWK | ✅ | ✅ | ✅ |
| Trusted JWT Grant Issuer | ✅ | ✅ | ✅ |
| Organization | ❌ | ✅* | ✅ |
| Relationship (Keto) | ❌ | ✅ | ✅ |
| Event Stream | ❌ | ❌ | ✅ |
*Organizations require B2B features to be enabled on your plan.
Time-based attributes (e.g., session_lifespan, oauth2_access_token_lifespan) use Go duration strings. The Ory API normalizes durations on write, so use the full normalized format to avoid perpetual diffs in terraform plan:
| Write | API Returns | Use in Config |
|---|---|---|
1h |
1h0m0s |
1h0m0s |
30m |
30m0s |
30m0s |
720h |
720h0m0s |
720h0m0s |
ory_identity_schema: Content is immutable; changes require resource replacement. Delete not supported by Ory API (resource removed from state only).ory_workspace: Delete not supported by Ory API.ory_oauth2_client:client_secretonly returned on initial creation.ory_email_template: Delete resets to Ory defaults rather than removing.ory_relationship: Requires Ory Permissions (Keto) to be enabled on the project.ory_project_config: Cannot be deleted — it always exists for a project. Only attributes present in your Terraform configuration are tracked for drift.
Documentation is auto-generated from templates using tfplugindocs. Do NOT edit files in docs/ directly — they are overwritten on every build.
To update documentation:
- Edit the templates in
templates/(e.g.,templates/resources/oauth2_client.md.tmpl) - Edit example files in
examples/(e.g.,examples/resources/ory_oauth2_client/resource.tf) - Run
make formatto regeneratedocs/from the templates
Templates use Go template syntax with these variables:
{{ .SchemaMarkdown | trimspace }}— auto-generated schema table from Go code{{ tffile "examples/resources/ory_foo/resource.tf" }}— embed example files{{ .Name }},{{ .Type }}— resource name and type
templates/
├── index.md.tmpl # Provider-level docs
├── resources/ # 16 resource templates
│ ├── action.md.tmpl
│ ├── email_template.md.tmpl
│ ├── event_stream.md.tmpl
│ ├── identity.md.tmpl
│ ├── identity_schema.md.tmpl
│ ├── json_web_key_set.md.tmpl
│ ├── oauth2_client.md.tmpl
│ ├── oidc_dynamic_client.md.tmpl
│ ├── organization.md.tmpl
│ ├── project.md.tmpl
│ ├── project_api_key.md.tmpl
│ ├── project_config.md.tmpl
│ ├── relationship.md.tmpl
│ ├── social_provider.md.tmpl
│ ├── trusted_oauth2_jwt_grant_issuer.md.tmpl
│ └── workspace.md.tmpl
└── data-sources/ # 6 data source templates
├── identity.md.tmpl
├── identity_schemas.md.tmpl
├── oauth2_client.md.tmpl
├── organization.md.tmpl
├── project.md.tmpl
└── workspace.md.tmpl
Run these checks locally before committing. They mirror what CI runs on every push.
# Minimum before committing:
make build && make format && make test
# Full CI-equivalent check:
make build && make format && make test && make sec && make licensesmake format runs several tools in sequence: go fmt, gofmt -s, terraform fmt, go mod tidy, tfplugindocs generate, and golangci-lint --fix.
make sec # Run all security scans
make sec-vuln # govulncheck — known Go vulnerabilities
make sec-gosec # gosec — Go security patterns
make sec-gitleaks # gitleaks — hardcoded secrets
make sec-trivy # trivy — vulnerability and misconfig scanningContributions are welcome! See CONTRIBUTING.md for detailed guidelines on development setup, testing, writing acceptance tests, and the contribution checklist.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Run checks:
make build && make format && make test - Commit using Conventional Commits format
- Open a Pull Request