← Main Guide Terraform Vault & Secrets API & SDKs Parent/Child

What the Terraform Provider Does

The StrongDM Terraform provider lets you define your entire StrongDM organization in HCL and manage it through standard Terraform workflows. Instead of clicking through the Admin UI, you declare your desired state in code and terraform apply makes it real.

What You Can Manage

N
Nodes
Create gateways and relays. Tokens are output for installation scripts.
R
Resources
Databases, servers, clusters, clouds, websites. Every resource type supported in the Admin UI works in Terraform.
U
Users & Service Accounts
Create, manage, and suspend user accounts. Create service accounts for automation.
A
Access
Roles, access rules (static and dynamic), account attachments, and access workflows.
Versioning

StrongDM uses semantic versioning. There is no compatibility guarantee between major versions. Always pin your provider version constraint to the major version you're using.


Provider Setup

Step 1: Generate API Keys

In the StrongDM Admin UI, go to Settings > Admin Tokens. Create a new token with the permissions you need (at minimum: Resources Create/List/Update, Relays Create/List, Accounts Create/List/Update, Roles Create/List/Update). Copy the access key and secret key. The secret is shown only once.

Step 2: Configure the Provider

There are two ways to supply credentials. Environment variables are the best practice -- they keep secrets out of your HCL files and state.

terminal
export SDM_API_ACCESS_KEY="<YOUR_ACCESS_KEY>"
export SDM_API_SECRET_KEY="<YOUR_SECRET_KEY>"
terraform plan
main.tf
terraform {
  required_providers {
    sdm = {
      source  = "strongdm/sdm"
      version = "~> 15.0"
    }
  }
}

provider "sdm" {
  # Credentials sourced from SDM_API_ACCESS_KEY
  # and SDM_API_SECRET_KEY environment variables
}
main.tf
terraform {
  required_providers {
    sdm = {
      source  = "strongdm/sdm"
      version = "~> 15.0"
    }
  }
}

provider "sdm" {
  api_access_key = "njjSn...5hM"
  api_secret_key = "ziG...="
}
Warning

Inline keys end up in your state file. Only use this for throwaway test environments. For anything real, use environment variables or a secrets manager.

Step 3: Init and Plan

terminal
terraform init    # Downloads the sdm provider
terraform plan    # Shows what will be created
terraform apply   # Creates the resources

Nodes (Gateways & Relays)

Both gateways and relays use the sdm_node resource type with different nested blocks.

Gateway

A gateway needs a listen_address (public IP/DNS + port that clients connect to) and optionally a bind_address (what the daemon actually binds to on the host).

gateway.tf
resource "sdm_node" "prod_gateway" {
  gateway {
    name           = "prod-gateway-01"
    listen_address = "gateway.example.com:5000"
    bind_address   = "0.0.0.0:5000"
    tags = {
      env    = "production"
      region = "us-east-1"
    }
  }
}

# Output the token for installation
output "gateway_token" {
  value     = sdm_node.prod_gateway.gateway[0].token
  sensitive = true
}

Relay

A relay is simpler: no listen address (it's egress-only).

relay.tf
resource "sdm_node" "private_relay" {
  relay {
    name = "private-subnet-relay-01"
    tags = {
      env    = "production"
      subnet = "private-10.0.7.0"
    }
  }
}

output "relay_token" {
  value     = sdm_node.private_relay.relay[0].token
  sensitive = true
}
Token Handling

When Terraform creates a gateway or relay, StrongDM generates a token. Use terraform output -raw gateway_token to retrieve it for installation. The token is stored in Terraform state, so treat your state file as sensitive.


Resources (Databases, Servers, Clusters, Websites)

Every resource type from the Admin UI has a corresponding Terraform resource. The resource type name follows the pattern sdm_resource with a nested block for the specific type.

PostgreSQL

postgres.tf
resource "sdm_resource" "prod_pg" {
  postgres {
    name     = "prod-postgres"
    hostname = "pg.internal.example.com"
    port     = 5432
    database = "production"
    username = "sdm_user"
    password = "secret"
    tags = {
      env  = "production"
      team = "platform"
    }
  }
}

MySQL

mysql.tf
resource "sdm_resource" "prod_mysql" {
  mysql {
    name     = "prod-mysql"
    hostname = "mysql.internal.example.com"
    port     = 3306
    database = "appdata"
    username = "sdm_user"
    password = "secret"
    tags = {
      env = "production"
    }
  }
}

Microsoft SQL Server (Azure AD)

mssql-azure.tf
resource "sdm_resource" "prod_mssql" {
  sql_server_azure_ad {
    name      = "sqlserver-azprod"
    hostname  = "sqlserver-azprod.database.windows.net"
    port      = 1433
    client_id = "your-app-client-id"
    secret    = "your-app-client-secret"
    tags = {
      env  = "production"
      team = "appdata"
    }
  }
}

SSH (Public Key)

ssh.tf
resource "sdm_resource" "web_server" {
  ssh {
    name     = "web-server-01"
    hostname = "10.0.1.50"
    port     = 22
    username = "ubuntu"
    tags = {
      env  = "production"
      role = "web"
    }
  }
}

SSH (Certificate Based)

ssh-cert.tf
resource "sdm_resource" "app_server" {
  ssh_cert {
    name     = "app-server-01"
    hostname = "10.0.1.51"
    port     = 22
    username = "ubuntu"
    tags = {
      env  = "production"
      role = "app"
    }
  }
}

RDP (Password)

rdp.tf
resource "sdm_resource" "win_server" {
  rdp {
    name     = "win-server-01"
    hostname = "10.0.2.10"
    port     = 3389
    username = "CORP\\admin"
    password = "P@ssw0rd"
    tags = {
      env = "production"
      os  = "windows"
    }
  }
}

RDP (Certificate Based)

rdp-cert.tf
resource "sdm_resource" "win_cert_server" {
  rdp_cert {
    name     = "win-server-02"
    hostname = "10.0.2.11"
    port     = 3389
    username = "CORP\\svc-sdm"
    password = "P@ssw0rd"
    tags = {
      env = "production"
    }
  }
}

EKS Cluster

eks.tf
resource "sdm_resource" "prod_eks" {
  amazon_eks {
    name                = "prod-eks-cluster"
    endpoint            = "https://ABCDEF.gr7.us-east-1.eks.amazonaws.com"
    cluster_name        = "my-cluster"
    region              = "us-east-1"
    certificate_authority = file("ca.pem")
    access_key          = "AKIAXXXXXXXX"
    secret_access_key   = "secret"
    tags = {
      env = "production"
    }
  }
}

EKS (Instance Profile - No Static Creds)

eks-iam.tf
resource "sdm_resource" "prod_eks_iam" {
  amazon_eks_instance_profile {
    name                = "prod-eks-iam"
    endpoint            = "https://ABCDEF.gr7.us-east-1.eks.amazonaws.com"
    cluster_name        = "my-cluster"
    region              = "us-east-1"
    certificate_authority = file("ca.pem")
    tags = {
      env = "production"
    }
  }
}

Website (HTTP)

website.tf
resource "sdm_resource" "internal_wiki" {
  website_http {
    name           = "internal-wiki"
    url            = "http://wiki.internal.example.com"
    http_subdomain = "wiki"
    health_path    = "/health"
    default_path   = "/home"
    tags = {
      env  = "production"
      type = "internal-tool"
    }
  }
}

Users, Roles, and Access

Creating Users

users.tf
resource "sdm_account" "jane" {
  user {
    first_name = "Jane"
    last_name  = "Doe"
    email      = "[email protected]"
  }
}

resource "sdm_account" "john" {
  user {
    first_name = "John"
    last_name  = "Doe"
    email      = "[email protected]"
  }
}

# Service account (for automation, no human login)
resource "sdm_account" "ci_bot" {
  service {
    name = "ci-deploy-bot"
  }
}

Creating Roles

roles.tf
resource "sdm_role" "sre_team" {
  name = "SRE Team"
}

resource "sdm_role" "db_team" {
  name = "Database Team"
}

Attaching Users to Roles

attachments.tf
resource "sdm_account_attachment" "jane_sre" {
  account_id = sdm_account.jane.id
  role_id    = sdm_role.sre_team.id
}

resource "sdm_account_attachment" "john_db" {
  account_id = sdm_account.john.id
  role_id    = sdm_role.db_team.id
}

Access Rules

Access rules define which resources a role grants access to. There are two styles:

Static Access Rules

Grant access to specific resources by ID.

static-access.tf
# Grant SRE team access to 
# specific resources
resource "sdm_role_access_rule" "sre_web" {
  role_id = sdm_role.sre_team.id
  resource_ids = [
    sdm_resource.web_server.id,
    sdm_resource.prod_pg.id,
  ]
}

Dynamic Access Rules

Grant access based on resource type and tags. New matching resources are automatically included.

dynamic-access.tf
# Grant DB team access to all
# production databases
resource "sdm_role_access_rule" "db_prod" {
  role_id = sdm_role.db_team.id
  tags = {
    env = "production"
  }
  # Optional: filter by type
  # resource_type = "postgres"
}
Deprecation Note

sdm_role_grant (granting access by individual resource ID) has been deprecated in favor of sdm_role_access_rule. Access Rules support both static (by ID) and dynamic (by tags/type) patterns and are the current best practice.


Production Patterns

Full Tenant Bootstrap

This is the pattern for spinning up a complete StrongDM environment from scratch in a single apply:

bootstrap.tf
# 1. Gateway
resource "sdm_node" "gw" {
  gateway {
    name           = "primary-gateway"
    listen_address = "sdm-gw.example.com:5000"
    bind_address   = "0.0.0.0:5000"
  }
}

# 2. Resources
resource "sdm_resource" "app_db" {
  postgres {
    name     = "app-database"
    hostname = "pg.internal.example.com"
    port     = 5432
    database = "myapp"
    username = "sdm"
    password = var.db_password
    tags     = { env = "production", team = "platform" }
  }
}

resource "sdm_resource" "app_server" {
  ssh_cert {
    name     = "app-server"
    hostname = "10.0.1.50"
    port     = 22
    username = "deploy"
    tags     = { env = "production", team = "platform" }
  }
}

# 3. Roles with dynamic access rules
resource "sdm_role" "platform" {
  name = "Platform Team"
}

resource "sdm_role_access_rule" "platform_prod" {
  role_id = sdm_role.platform.id
  tags    = { env = "production", team = "platform" }
}

# 4. Users
resource "sdm_account" "alice" {
  user {
    first_name = "Alice"
    last_name  = "Engineer"
    email      = "[email protected]"
  }
}

resource "sdm_account_attachment" "alice_platform" {
  account_id = sdm_account.alice.id
  role_id    = sdm_role.platform.id
}

Variables and Secrets

variables.tf
variable "db_password" {
  type      = string
  sensitive = true
}

variable "environment" {
  type    = string
  default = "production"
}

Pass secrets via environment: TF_VAR_db_password="secret" terraform apply

Importing Existing Resources

If you have resources already created in the Admin UI and want to bring them under Terraform management:

terminal
# Find the resource ID from Admin UI or CLI
sdm admin resources list

# Import into Terraform state
terraform import sdm_resource.my_pg rs-1234567890abcdef

# Then write the matching HCL to keep it in sync

CI/CD Integration

github-actions.yml
- name: Terraform Apply
  env:
    SDM_API_ACCESS_KEY: ${{ secrets.SDM_ACCESS_KEY }}
    SDM_API_SECRET_KEY: ${{ secrets.SDM_SECRET_KEY }}
  run: |
    terraform init
    terraform apply -auto-approve
Tip

Use dynamic access rules with tags. When your CI/CD pipeline creates new infrastructure (e.g., a new RDS instance), tag it consistently. The dynamic access rule automatically picks it up without needing a Terraform change on the StrongDM side.


Quick Reference: Resource Type Names

ResourceTerraform Nested Block
PostgreSQLpostgres
MySQLmysql
SQL Serversql_server
SQL Server (Azure AD)sql_server_azure_ad
MongoDBmongo_legacy_host
Redisredis
DynamoDBdynamo_db
Cassandracassandra
SSH (Public Key)ssh
SSH (Certificate)ssh_cert
SSH (Customer Key)ssh_customer_key
RDP (Password)rdp
RDP (Certificate)rdp_cert
EKSamazon_eks
EKS (Instance Profile)amazon_eks_instance_profile
AKSaks
GKEgoogle_gke
Website (HTTP)website_http
Website (Basic Auth)website_http_basic_auth
Gatewaysdm_node > gateway
Relaysdm_node > relay
Usersdm_account > user
Service Accountsdm_account > service
Rolesdm_role
Access Rulesdm_role_access_rule
Account Attachmentsdm_account_attachment

Note

Content sourced from docs.strongdm.com and the strongdm/terraform-provider-sdm GitHub repo, March 2026. For the latest provider version and full resource schema, check the Terraform Registry.