Article · part of a guide
How to use the Terraform Okta Provider
Automate Okta with Terraform: step-by-step code, security best practices, and tips from a developer’s guide to resilient identity management.

Key takeaways
- The Okta Terraform provider (okta/okta) manages core Okta resources including users, groups, applications, and policies through the standard init, plan, and apply workflow.
- Two authentication methods exist: a static API token (SSWS) suitable only for testing, and OAuth 2.0 with a private key, which is recommended for all environments because it supports granular scope-based access.
- Never hardcode secrets; use a secrets manager like HashiCorp Vault or environment variables the provider auto-detects, such as OKTA_CLIENT_ID and OKTA_PRIVATE_KEY.
- Set the priority argument explicitly on policies of the same type, since Okta evaluates them lowest-number-first, and use depends_on to control creation order.
- Manage API rate limits with the max_api_capacity provider argument, detect drift by running terraform plan frequently, and store state remotely for any team or production use.
This guide walks through setting up the Okta Terraform provider, authenticating to it, and managing the core Okta resources you'll touch most often: users, groups, applications, and policies. It also gets into operational practices, CI/CD integration, and the errors that tend to come up.
How Do You Set Up the Okta Terraform Provider?
You'll need to set up two things: your Okta account and your local Terraform environment.
Prerequisites
- Okta Account: You need an Okta organization. For testing and development, grab a free Okta Developer Account. For production, use an existing corporate Okta account with the right admin permissions.
- Terraform: Install the Terraform CLI locally. Use the latest stable version.
It can be helpful to use a TACO as well.
Okta Terraform Provider Configuration
The Okta Terraform provider comes from the official Terraform Registry. To configure it, create a file named versions.tf and set the provider source and version.
// versions.tf
terraform {
required_providers {
okta = {
source = "okta/okta"
version = "~> 4.17.0" // Check the Terraform Registry for the latest version
}
}
}Running terraform init in your configuration directory will download and install the specified provider version.
Provider Authentication
The provider has to authenticate to your Okta organization's API. There are two main methods: API Token and OAuth 2.0. Use OAuth 2.0 for all environments, especially production.
Method 1: API Token (SSWS)
This method uses a static API token generated from the Okta admin console (Security > API > Tokens).
- Security Concern: The token inherits all permissions of the user who created it, so it's hard to enforce least privilege. It's fine for early testing in a dev environment, but don't use it in production.
Configuration:
// main.tf
provider "okta" {
org_name = "your-org-name" // e.g., dev-123456
base_url = "okta.com" // e.g., okta.com, oktapreview.com
api_token = var.okta_api_token // Provided as a variable
}
variable "okta_api_token" {
description = "Okta API Token"
type = string
sensitive = true
}Method 2: OAuth 2.0 with a Private Key
This method uses an Okta API Service Application set up for the OAuth 2.0 client credentials flow with a public/private key pair. It gives you granular, scope-based access control.
Setup Steps:
- Create an Okta Service App: In the Okta Admin UI, navigate to Applications > Create App Integration, select "API Services", and save the new application.
- Generate a Key Pair: Use a tool like OpenSSL or have Okta generate a key pair. The private key must be in unencrypted PKCS#1 or PKCS#8 PEM format. Securely store the private key. Okta will store the public key.
- Grant API Scopes: In the application's "Okta API Scopes" tab, grant the minimum required scopes for your Terraform operations (e.g.,
okta.groups.manage,okta.apps.read).
Configuration:
// main.tf
provider "okta" {
org_name = "your-org-name"
base_url = "okta.com"
client_id = var.okta_oauth_client_id
private_key = var.okta_oauth_private_key
scopes = ["okta.groups.manage", "okta.apps.read", "okta.policies.manage"]
}
variable "okta_oauth_client_id" {
description = "Client ID for the Okta Service App"
type = string
sensitive = true
}
variable "okta_oauth_private_key" {
description = "Private key (PEM format) for the Okta Service App"
type = string
sensitive = true
}Securing Credentials
Never hardcode secrets in configuration files. The best approach is a secrets management tool like HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault. You can also use environment variables, which the Okta provider picks up automatically (OKTA_CLIENT_ID, OKTA_PRIVATE_KEY, etc.).
Which Okta Resources Can You Manage with Terraform, Including okta_app_oauth?
You manage Okta resources with the standard Terraform workflow: init, plan, and apply. Read the output of terraform plan carefully, since it's what keeps you from making unintended changes to your identity infrastructure.
Common Okta Objects and Terraform Resources
| Okta Object | Terraform Resource(s) | Key Arguments/Purpose | Example Required Scopes |
|---|---|---|---|
| User | okta_user, okta_user_schema_property |
login, email, firstName, lastName, status, custom_profile_attributes |
okta.users.manage, okta.users.read, okta.userSchemas.manage |
| Group | okta_group, okta_group_schema_property |
name, description, custom_profile_attributes |
okta.groups.manage, okta.groups.read, okta.groupSchemas.manage |
| Group Membership | okta_group_memberships, okta_user_group_memberships |
group_id, user_id, users (list), groups (list) |
okta.groups.manage |
| OIDC Application | okta_app_oauth |
label, type, grant_types, redirect_uris, token_endpoint_auth_method |
okta.apps.manage, okta.apps.read |
| SAML Application | okta_app_saml |
label, preconfigured_app, sso_url, audience, attribute_statements |
okta.apps.manage, okta.apps.read |
| Sign-On Policy | okta_policy_signon |
name, status, priority, groups_included, session settings |
okta.policies.manage, okta.policies.read |
| MFA Enrollment Policy | okta_policy_mfa |
name, status, priority, groups_included, factor enrollment settings |
okta.policies.manage, okta.policies.read |
| Policy Rule | okta_policy_rule_signon, okta_policy_rule_mfa |
policy_id, name, priority, conditions and actions |
okta.policies.manage, okta.policies.read |
| Authenticator | okta_authenticator |
name, key, status, settings |
okta.authenticators.manage, okta.authenticators.read |
Managing Groups and Memberships
Groups are the backbone of access control. You can manage memberships from the group side or the user side.
// groups.tf
resource "okta_group" "engineering_team" {
name = "Engineering Team"
description = "All members of the engineering department"
}
// User resources (e.g., okta_user.jdoe) must be defined elsewhere
resource "okta_group_memberships" "engineering_team_members" {
group_id = okta_group.engineering_team.id
users = [
okta_user.jdoe.id,
okta_user.jsmith.id
]
}Managing OIDC & SAML with Terraform
You can put application configs in code, which keeps them consistent across environments.
OIDC Service Application:
// apps_oidc.tf
resource "okta_app_oauth" "backend_api_service" {
label = "Backend API Service"
type = "service"
grant_types = ["client_credentials"]
token_endpoint_auth_method = "client_secret_basic"
}More on OIDC.
SAML Application:
// apps_saml.tf
resource "okta_app_saml" "aws_account_federation" {
preconfigured_app = "amazon_aws"
label = "AWS Account Federation (SAML)"
attribute_statements {
name = "https://aws.amazon.com/SAML/Attributes/Role"
namespace = "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
values = ["arn:aws:iam::123456789012:saml-provider/Okta,arn:aws:iam::123456789012:role/OktaPowerUser"]
}
}Managing Okta Password Policies
Policies and their rules are the core of Okta's security posture.
// policies.tf
data "okta_group" "everyone" {
name = "Everyone"
}
resource "okta_policy_password" "strong_password_policy" {
name = "Strong Password Policy"
status = "ACTIVE"
groups_included = [data.okta_group.everyone.id]
password_min_length = 14
password_min_lowercase = 1
password_min_uppercase = 1
password_min_number = 1
password_min_symbol = 1
password_history_count = 6
password_max_age_days = 90
}Example password policy with Terraform
One thing that matters a lot in policy management is priority. Okta evaluates policies of the same type in order of their priority value (lowest number wins). To get a deterministic order, set the priority argument explicitly on every policy of the same type, and use the depends_on meta-argument to control creation order.
// policies.tf
resource "okta_policy_signon" "contractor_access_policy" {
name = "Contractor Access Policy"
priority = 10
status = "ACTIVE"
# ... other settings
}
resource "okta_policy_signon" "employee_access_policy" {
name = "Employee Access Policy"
priority = 20
status = "ACTIVE"
# ... other settings
depends_on = [okta_policy_signon.contractor_access_policy]
}What Operational Practices Keep Okta Terraform Running Smoothly?
Managing Okta with Terraform over the long run takes a few habits beyond the basic workflow.
Code Structure and Modules
Split your codebase into files that make sense (e.g., variables.tf, apps_oidc.tf, policies.tf). For reusable components, build Terraform modules. That keeps things consistent and easier to maintain. To manage multiple Okta environments (dev, prod), use Terraform Workspaces, which let you keep separate state files for the same configuration.
State File Management
The Terraform state file maps your configuration to real-world resources. For any team or production use, you have to store the state file remotely. Remote backends like Terraform Cloud, AWS S3, or Azure Blob Storage give you state locking (so two runs can't clobber each other), secure storage, and shared access.
S3 Backend Example:
// backend.tf
terraform {
backend "s3" {
bucket = "my-secure-okta-terraform-state"
key = "okta/production/terraform.tfstate"
region = "us-west-2"
dynamodb_table = "terraform-okta-state-locks"
encrypt = true
}
}Managing Configuration Drift
Configuration drift happens when the actual state of your Okta resources no longer matches the state in your code, usually because someone made a manual change in the Okta UI.
- Detection: Run
terraform planoften. The plan output shows any discrepancies. - Mitigation: The main rule is that resources managed by Terraform should only be changed by Terraform. If you do need to pull in a manual change, use
terraform importto bring the resource's current state under Terraform management.
More on how to detect and remediate Terraform drift.
API Rate Limits
Terraform can fire off a lot of API calls and hit Okta's rate limits. To manage this:
- Optimize Code: Remove resources you don't need from your configuration to cut down the read operations during a plan.
- Tune the Provider: Use the
max_api_capacityargument in the provider block to tell Terraform to pause when it gets close to a set percentage of your organization's API rate limit. - Use Custom App Limits: Set a custom rate limit on the service application Terraform uses, right in the Okta admin console, as a safeguard.
CI/CD Integration
Automate your Okta changes through a CI/CD pipeline. A typical workflow looks like this:
- Pull Request: A developer opens a pull request with configuration changes.
- Plan Stage: The pipeline automatically runs
terraform planand posts the output to the pull request for review. - Apply Stage: After approval and merge, the pipeline runs
terraform apply -auto-approveto deploy the changes to the target environment.
Conceptual GitHub Actions Step:
- name: Terraform Plan
id: plan
run: terraform plan -no-color
env:
OKTA_CLIENT_ID: ${{ secrets.OKTA_CLIENT_ID }}
OKTA_PRIVATE_KEY: ${{ secrets.OKTA_PRIVATE_KEY_PEM }}
# ... other env varsSee the using Gitlab with Terraform guide for more info.
How Do You Troubleshoot Common Okta Provider Errors?
When you hit errors, turning on debug logging tells you the most.
# Enable debug logging for the current command
TF_LOG=DEBUG terraform plan
# Log debug output to a file
TF_LOG=DEBUG TF_LOG_PATH="terraform-debug.log" terraform applyWatch out: debug logs can contain sensitive data. Redact secrets before you share logs.
Common errors usually come down to:
- Permissions: The service app or API token lacks the required Okta API scopes or admin roles. Solution: Verify and grant necessary permissions in the Okta admin console.
- Provider Upgrades: The state file may be incompatible with a new provider version. Solution: Use
terraform plan -replace="resource.address"to force a recreation of the problematic resource, which refreshes its state. - Resource Not Found: An ID is incorrect, or the resource was manually deleted. Solution: Verify the resource ID and that the resource exists in Okta. Use
depends_onto manage dependencies between resources created in the same configuration.
Using GitLab? You might be interested in the following guide too:
About the author

CEO at Scalr
Sebastian Stadil is the CEO of Scalr with 15+ years of DevOps experience. He started with AWS in 2004 and advised early Microsoft Azure and Google Cloud.
Part of this guide
16 sheets
Terraform Providers: Complete Configuration and Management Guide
- How to Manage GitLab with Terraform
- Mastering Kubernetes with Terraform: A Provider Deep Dive
- Top 10 Most Popular Terraform Providers [2026]
- What are Terraform Lock Files
- AzureRM Terraform Provider Overview
- Kubernetes Terraform Provider
- Terraform Random Provider
- How to use the Bitbucket Terraform Provider
- How to use Terraform or OpenTofu to Manage Datadog
- Getting Started with Terraform Providers
- Getting Started with the Google Cloud Terraform Provider
- How to use Terraform to manage Okta
- New Feature: Provider Configurations
- Using A Custom Terraform Provider In Scalr