StrongDM Terraform Provider
Manage your entire StrongDM organization as code. This guide covers provider setup, resource definitions for every type, access configuration, and production patterns.
strongdm/sdm on Terraform Registry
Requires
Terraform 0.13+
Updated
March 2026
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
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.
export SDM_API_ACCESS_KEY="<YOUR_ACCESS_KEY>"
export SDM_API_SECRET_KEY="<YOUR_SECRET_KEY>"
terraform planterraform {
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
}terraform {
required_providers {
sdm = {
source = "strongdm/sdm"
version = "~> 15.0"
}
}
}
provider "sdm" {
api_access_key = "njjSn...5hM"
api_secret_key = "ziG...="
}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
terraform init # Downloads the sdm provider
terraform plan # Shows what will be created
terraform apply # Creates the resourcesNodes (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).
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).
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
}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
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
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)
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)
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)
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)
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)
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
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)
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)
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
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
resource "sdm_role" "sre_team" {
name = "SRE Team"
}
resource "sdm_role" "db_team" {
name = "Database Team"
}Attaching Users to Roles
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.
# 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.
# 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"
}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:
# 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
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:
# 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 syncCI/CD Integration
- 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-approveUse 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
| Resource | Terraform Nested Block |
|---|---|
| PostgreSQL | postgres |
| MySQL | mysql |
| SQL Server | sql_server |
| SQL Server (Azure AD) | sql_server_azure_ad |
| MongoDB | mongo_legacy_host |
| Redis | redis |
| DynamoDB | dynamo_db |
| Cassandra | cassandra |
| SSH (Public Key) | ssh |
| SSH (Certificate) | ssh_cert |
| SSH (Customer Key) | ssh_customer_key |
| RDP (Password) | rdp |
| RDP (Certificate) | rdp_cert |
| EKS | amazon_eks |
| EKS (Instance Profile) | amazon_eks_instance_profile |
| AKS | aks |
| GKE | google_gke |
| Website (HTTP) | website_http |
| Website (Basic Auth) | website_http_basic_auth |
| Gateway | sdm_node > gateway |
| Relay | sdm_node > relay |
| User | sdm_account > user |
| Service Account | sdm_account > service |
| Role | sdm_role |
| Access Rule | sdm_role_access_rule |
| Account Attachment | sdm_account_attachment |
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.