
This article is part of a series on Terraform Providers.
This guide covers the setup process, authentication methods, and management of core Okta resources (users, groups, applications, policies). It also covers best practices, and CI/CD integration and troubleshooting.
Proper setup requires configuring both your Okta account and local Terraform environment.
It can be helpful to use a TACO as well.
The Okta Terraform provider is sourced from the official Terraform Registry. To configure it, create a file named versions.tf and specify 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.
The provider requires authentication to your Okta organization's API. There are two primary methods: API Token and OAuth 2.0. OAuth 2.0 is the recommended method for all environments, especially production.
This method uses a static API token generated from the Okta admin console (Security > API > Tokens).
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
}This method uses an Okta API Service Application configured for the OAuth 2.0 client credentials flow with a public/private key pair. It allows for granular, scope-based access control.
Setup Steps:
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
}Never hardcode secrets in configuration files. The recommended approach is to use a secrets management tool like HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault. Alternatively, use environment variables, which the Okta provider will automatically detect (OKTA_CLIENT_ID, OKTA_PRIVATE_KEY, etc.).
The standard Terraform workflow of init, plan, and apply is used to manage Okta resources. Rigorously reviewing the output of terraform plan is critical to prevent unintended changes to your identity infrastructure.
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
Groups are fundamental for access control. Memberships can be managed from the perspective of a group or a user.
// 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
]
}Application configurations can be codified, ensuring consistency 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"]
}
}Policies and their associated rules form 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
A critical aspect of policy management is priority. Okta evaluates policies of the same type in order of their priority value (lowest number is highest priority). To ensure a deterministic order, explicitly set the priority argument for all policies 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]
}Effective long-term management requires adopting several key operational practices.
Organize your codebase into logical files (e.g., variables.tf, apps_oidc.tf, policies.tf). For reusable components, create Terraform modules. This promotes consistency and simplifies maintenance. To manage multiple Okta environments (dev, prod), use Terraform Workspaces, which allow you to maintain separate state files for the same configuration.
The Terraform state file maps your configuration to real-world resources. For any team-based or production use, storing the state file remotely is mandatory. Remote backends like Terraform Cloud, AWS S3, or Azure Blob Storage provide state locking (to prevent concurrent runs), 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
}
}Configuration drift occurs when the actual state of Okta resources diverges from the state defined in your code, typically due to manual changes in the Okta UI.
terraform plan frequently. The plan output will show any discrepancies.terraform import command to bring the resource's current state under Terraform management.More on how to handle Terraform drift here or read the in-depth guide on Terraform drift.
Terraform can generate a high volume of API calls, potentially hitting Okta's rate limits. To manage this:
max_api_capacity argument in the provider block to instruct Terraform to pause if it approaches a specified percentage of your organization's API rate limit.Automate your Okta changes through a CI/CD pipeline. A typical workflow includes:
terraform plan and posts the output to the pull request for review.terraform apply -auto-approve to 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.
When encountering errors, enabling debug logging provides the most insight.
# 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 applyBe aware that debug logs can contain sensitive data. Redact secrets before sharing logs.
Common errors often relate to:
terraform plan -replace="resource.address" to force a recreation of the problematic resource, which refreshes its state.depends_on to manage dependencies between resources created in the same configuration.Using GitLab? You might be interested in the following guide too:
