Documentation Index
Fetch the complete documentation index at: https://docs.runlayer.com/llms.txt
Use this file to discover all available pages before exploring further.
Beta access
The Runlayer Terraform provider is currently in beta.
To get access:
We will help with:
- Private network access if your Runlayer API is not public
- Access to the provider release
- The right base URL for your environment
What you need
- Terraform installed
- Network access to your Runlayer API
- A Runlayer user API key
- A provider binary from the GitHub releases page for
runlayer/terraform-provider-runlayer
Setup
Get private access first
If your Runlayer API is only reachable over VPN or another private network, make sure that is working before you start Terraform.
Download the provider release
Download the correct release artifact for your OS from the GitHub releases page for runlayer/terraform-provider-runlayer.Example:gh release download \
--repo runlayer/terraform-provider-runlayer \
--pattern "*darwin_arm64.zip" \
--output provider.zip
unzip provider.zip -d /tmp/tf-provider
mv /tmp/tf-provider/terraform-provider-runlayer_v* /tmp/tf-provider/terraform-provider-runlayer
The binary should end up named terraform-provider-runlayer. Create a local override directory
Put the provider binary in its own directory. Example:mkdir -p ~/.runlayer/terraform-provider
mv ./terraform-provider-runlayer ~/.runlayer/terraform-provider/
chmod +x ~/.runlayer/terraform-provider/terraform-provider-runlayer
Tell Terraform to use the local provider
Create a Terraform CLI config file:provider_installation {
dev_overrides {
"stainless-sdks/runlayer" = "/Users/you/.runlayer/terraform-provider"
}
direct {}
}
Then point Terraform at it:export TF_CLI_CONFIG_FILE="$HOME/.terraformrc"
Export your Runlayer credentials
export RUNLAYER_API_KEY="rl_..."
export RUNLAYER_BASE_URL="https://your-runlayer-base-url"
Use a user API key here.Organization API keys are not sufficient for Terraform resource management endpoints such as policy creation.
RUNLAYER_BASE_URL should be the API base URL for your environment. If you are not sure which URL to use, ask us.
Create your Terraform config
Minimal example:terraform {
required_providers {
runlayer = {
source = "stainless-sdks/runlayer"
}
}
}
provider "runlayer" {}
Optional: set the base URL in the provider block
If you prefer explicit provider configuration instead of environment variables, use the provider’s base_url and api_key arguments:variable "runlayer_base_url" {
type = string
}
variable "runlayer_api_key" {
type = string
sensitive = true
}
provider "runlayer" {
base_url = var.runlayer_base_url
api_key = var.runlayer_api_key
}
If you use this form, the variable name can be runlayer_base_url, but the provider argument itself is base_url. Run Terraform
When you use dev_overrides, go straight to terraform plan or terraform apply. Terraform may warn or fail if you run terraform init first.terraform providers schema -json > /dev/null
terraform plan
Verify the setup
A good first check is:
terraform providers schema -json works with your local override
- once you add a real resource,
terraform plan reaches the Runlayer API instead of failing on auth, host, provider install, or network setup
Using variables for users, groups, and roles
If you want stable references in Terraform, declare variables like this:
variable "users" {
type = map(string)
}
variable "groups" {
type = map(string)
}
variable "roles" {
type = map(string)
}
Then use them in resources:
principal = {
type = "group"
ids = [var.groups.engineers]
}
How the CLI can help
The CLI includes uvx runlayer terraform export to generate a tfvars file with stable names for users, groups, and roles.
Example:
uvx runlayer terraform export --output runlayer.auto.tfvars
Example output:
users = {
marcin_at_runlayer_com = "11111111-1111-1111-1111-111111111111"
}
groups = {
engineers = "22222222-2222-2222-2222-222222222222"
}
roles = {
admin = "33333333-3333-3333-3333-333333333333"
}
That file can be loaded by Terraform and referenced like:
var.users.marcin_at_runlayer_com
var.groups.engineers
var.roles.admin
This is useful when one person refreshes IDs occasionally and the rest of the Terraform code stays readable.
Policy examples
These examples are based on the provider e2e coverage and show common policy shapes.
Allow one group full access to one server
resource "runlayer_policy" "allow_engineers_linear" {
action = "allow"
principal = {
type = "group"
ids = [var.groups.engineers]
}
scope = {
servers = [runlayer_server.linear.id]
tools = ["*"]
resources = ["*"]
}
description = "Engineers: full Linear access"
}
Allow one group read-only access to a server
resource "runlayer_policy" "allow_contractors_deepwiki_readonly" {
action = "allow"
principal = {
type = "group"
ids = [var.groups.contractors]
}
scope = {
servers = [runlayer_server.deepwiki.id]
tools = ["search_documentation", "get_library_docs", "resolve_library_id"]
resources = ["*"]
}
description = "Contractors: read-only DeepWiki"
}
Deny one group access to one server
resource "runlayer_policy" "deny_contractors_linear" {
action = "deny"
principal = {
type = "group"
ids = [var.groups.contractors]
}
scope = {
servers = [runlayer_server.linear.id]
tools = ["*"]
resources = ["*"]
}
description = "Contractors: no Linear access"
}
resource "runlayer_policy" "deny_destructive_tools" {
action = "deny"
principal = {
type = "any"
}
scope = {
servers = [runlayer_server.linear.id]
tools = ["*"]
resources = ["*"]
}
conditions = [{
rules = [{
field = "tool_name"
operator = "begins_with"
value = "delete"
report_raw = false
}]
}]
description = "Deny any tool starting with delete"
}
Deny access outside a private IP range
resource "runlayer_policy" "deny_internal_outside_vpn" {
action = "deny"
principal = {
type = "any"
}
scope = {
servers = [runlayer_server.internal_tools.id]
tools = ["*"]
resources = ["*"]
}
conditions = [{
rules = [{
field = "meta.request.ip"
operator = "not_ip_range"
value = "10.0.0.0/8"
report_raw = true
}]
}]
description = "Deny internal tools access from outside VPN"
}
Deny inactive users globally
resource "runlayer_policy" "deny_inactive_users" {
action = "deny"
principal = {
type = "any"
}
scope = {
servers = ["*"]
tools = ["*"]
resources = ["*"]
}
conditions = [{
rules = [{
field = "meta.subject.is_active"
operator = "equals"
value = "false"
report_raw = false
}]
}]
description = "Global: deny all access for inactive users"
}
resource "runlayer_policy" "deny_contractors_internal_writes" {
action = "deny"
principal = {
type = "group"
ids = [var.groups.contractors]
}
scope = {
servers = [runlayer_server.internal_tools.id]
tools = ["*"]
resources = ["*"]
}
conditions = [{
rules = [
{
field = "tool_name"
operator = "not_equals"
value = "read_data"
report_raw = false
},
{
field = "tool_name"
operator = "not_equals"
value = "list_items"
report_raw = false
},
]
}]
description = "Contractors: deny internal tools except read_data and list_items"
}
Troubleshooting
terraform init fails under dev_overrides: skip init and run terraform plan directly
- Terraform cannot find the provider: check
TF_CLI_CONFIG_FILE and the dev_overrides path
terraform apply returns 403 Forbidden: make sure you are using a user API key, not an organization API key
- The provider binary does not run: make sure it is executable
- API calls fail: verify
RUNLAYER_API_KEY
- API calls hit the wrong environment: verify
RUNLAYER_BASE_URL
- Requests time out or refuse to connect: confirm private network access first