Managing Multiple Okta Instances with Terraform Cloud

Congratulations, you’ve chosen to use Okta to solve your identity problems. Welcome to the happy sunny utopia of a managed identity solution! But wait! How do you manage your environments? Your applications all have separate production, staging, and development environments. How do you manage that in Okta without writing a ton of custom scripts?

In this post, you’ll learn how to manage multiple Okta instances using Terraform and our new Okta Terraform Provider. I’ll walk you through everything you need to know to keep all your Okta environments in sync and manageable.

PS: The rest of this post assumes you already have at least one Okta account. If you don’t, you can create a free-forever developer account by visiting

Use Terraform to Manage Okta State

You have many options when it comes to picking a solution in the configuration management space: Chef, Puppet, Ansible, Salt, etc. Throughout this post, you’ll be working with Terraform as it sits in the sweet spot between orchestration and automation.

Instead of using the Okta Admin UI to configure all of your Okta settings and resources, you’ll define your Okta configuration in Terraform configuration files using the declarative HashiCorp Configuration Language (HCL). Terraform will then make the necessary API calls to Okta to configure things in the way you’ve defined, enabling you to automate the provisioning, deployment, and configuration of your Okta orgs.

If another administrator modifies your resources from the administrative console, you’ll be able to see the changes at a glance and revert them back to your defined state if you so choose.

To follow this guide, make sure that Terraform is installed and available on your path. If you don’t have Terraform installed and set up already, read through the official Hashicorp installation guide and set it up before continuing.

Get Started with Terraform and Okta

To get started, let’s create a simple Terraform script to update Okta’s user schema and define some custom attributes.

Before continuing, you’ll need to know your Okta organization name. Your Okta org will have a name like Make a note of this org name (dev-1234), as you’ll need it through this tutorial.

First, create a new local directory to hold your Terraform configuration files, then initialize Terraform. To do this, run the following commands:

mkdir okta-user-schema
cd okta-user-schema
terraform init

This will initialize the Terraform state file in your directory that tracks the configuration Terraform has applied to your resources. Next, create a file named and insert the code below. This file will hold your Okta configuration values that Terraform will use to talk to the Okta APIs.

org_name  = "<your-org>"
base_url  = ""
api_token = "<your-api-token>"

Replace each of the placeholder values above with those from your Okta org. For example, you should use the subdomain of your Okta org to fill in the org_name value. If the address of your Okta instance is then your org_name would be dev-1234. The value for base_url should be everything that comes after the org name (e.g., so be sure to update this value if you are using an okta-emea or oktapreview org. You will need to generate the api-token value.

To generate a new Okta API token, log into your Okta administrator console as a superuser and select API -> Tokens from the navigation menu. Next, click the Create Token button and give your token a name, then click Ok and copy the newly generated token into the configuration file above.

Next, create a new file named and add the following:

variable "org_name" {}
variable "api_token" {}
variable "base_url" {}

provider "okta" {
    org_name = var.org_name
    base_url = var.base_url
    api_token = var.api_token

This includes the Okta provider for Terraform and provides the three variables from our file to configure it.

Now, add the following to

resource "okta_user_schema" "dob_extension" {
  index  = "date_of_birth"
  title  = "Date of Birth"
  type   = "string"
  master = "PROFILE_MASTER"

This will extend the Okta user schema by adding a field to store users’ date of birth.

To test this script, run terraform plan from the command line. This should print a list of the changes the Terraform will make to your Okta org:

Terraform plan

Before you change anything, you’ll want to version control your new configuration. From the command line, run:

git init
git add
git commit -m "Initial commit. Adding the date of birth extension"

NOTE: Don’t push your file to source control. That file contains sensitive secrets like your Okta API credentials, etc. Add the *.tfvars into your .gitignore file so you don’t accidentally include it.

Now that you’ve defined your Terraform rules, you can apply these changes by running terraform apply on the command line. If you do this, Terraform will actually modify your user schema as defined above by talking to the Okta APIs. It will also update the state file on your local machine and keep track of the resources it creates.

Get Started with Terraform Cloud

Although working on your local machine is fine if you’re building a simple project solo, in many cases, you’ll want to use Terraform Cloud, which allows you have a nice Terraform workflow without needing to track state files locally. It’s incredibly convenient to use for any real projects.

Now let’s set up Terraform Cloud so you can configure your infrastructure without needing to worry about storing and managing local files.

If you don’t already have a Terraform Cloud account, you can sign up for a free account, which you can use with a team of up to five people, here.

Once you create an account and sign in, you are going to create a “Workspace”. Workspaces describe your environments (production, staging, development, etc.). The environment you’ll create below will be a “production” environment (we’ll create others later).

Create workspace using Terraform Cloud

On the first page of the Workspace creation flow, you need to select your source control provider. In my case, I’m using GitHub to store my code, so I’ll select GitHub and grant permission for Terraform Cloud to access my project.

Terraform Cloud select repository

Next, I’m going to select the code repository that holds my project.

Terraform Cloud name workspace

Give your new workspace a descriptive name. In this example, we’re defining our production environment, so use the repository’s name with -production added to the end for clarity. For example, my-project-production.

Locally you defined variables in the file. Since that file isn’t stored in source control, you have to explicitly define them. Click the Variables tab in the top navbar, then define the three variables you put into the file earlier.

Terraform Cloud set variables

For your API token, instead of using the same Okta SSWS token you used locally, you may want to create a separate token or even create a service account to issue a token so you can distinguish Terraform Cloud’s API usage from your own in your Okta logs. Whatever you do, make sure you mark the API token value as Sensitive when you define it so it isn’t exposed to anyone with access to your Terraform console.

Now that you’ve configured your workspace, select Queue plan from the top right, enter a reason, and then press Queue plan.

Terraform Cloud variables Terraform Cloud first run

Your Terraform plan will now run as it did in your local environment.

Terraform Cloud analyze changes

Before it applies the changes, however, Terraform will ask you to confirm the plan and leave a comment.

Terraform Cloud confirm changes Terraform Cloud leave comment

Once you click Confirm Plan, Terraform will run and apply your changes.

Terraform Cloud apply running

If you now look at your Okta user schema by clicking Users -> Profile Editor -> Profile in your Okta admin console, you’ll be able to see that Terraform has added the date of birth attribute!

Okta date of birth

Click Dashboard in your Okta tenant and scroll down to look at the Okta system log. You’ll now see that the configurations changes are logged as the user who minted the API token. Nice, right?

Okta logs

Another nice collaboration feature of Terraform Cloud is that you can leave comments on completed runs. This can be useful for discussing configuration changes with your team.

Terraform Cloud view comments

Manage Multiple Okta Environments with Terraform

Now that you’re using Terraform Cloud to manage a single environment (production), let’s add a second environment (development), which will make it easy for your developers to run tests against this new Okta environment without impacting production.

If you don’t have a second Okta org yet, go create one, and then go through the steps to create an API token once more.

Next, create a new Git branch in your project called dev. This is where you’ll work on your product before releasing it to production (the master branch).

git checkout -b dev
git push origin dev

In Terraform Cloud, create a new workspace to represent this new environment. Target the same repository as before but set the branch specifier to dev. For this environment, set the apply method in Terraform Cloud to auto so changes will be applied to the environment immediately if the plan stage is successful without you needing to manually confirm anything.

Terraform Cloud create workspace

This time, set up new variables for your workspace using your new Okta org’s credentials. Click Queue plan and just like that, Terraform replicated your production Okta configuration from your first Okta org into the second Okta org!

Promoting Changes Using Terraform

Now that you set up Terraform to manage both your development and production environments, let’s imagine your development team wants to make a change to how Okta is configured. You’d like them to be able to do this, but you’d also still like to control and review these changes before they make their way into production. What do you do?

To control this, you can use GitHub’s branch protection rules feature to ensure that you review any changes going onto our master branch that controls production before they are applied. This feature is only available on pro or public repos so you will need to make your repository public to follow along.

In your GitHub repository, click Settings -> Branches -> Add Rule and enter master as the branch name pattern to protect. You can then combine the rules you wish to use to protect the production configuration. Here we’re going to apply the Require pull request reviews before merging and Require status checks to pass before merging rules. With these two flags, you can make sure that one of the repository owners reviews changes before a merge into the master branch is performed and that the dev environment is in a good state.

GitHub branch protection

Let’s try the new change control process. On your dev branch, add the following change to the file:

resource "okta_user_schema" "crn_extension" {
  index  = "customer_reference_number"
  title  = "Customer Reference Number"
  required = true
  type   = "string"
  master = "PROFILE_MASTER"
  depends_on = [okta_user_schema.dob_extension]

Now save and push these changes to your remote.

Terraform will trigger and run your plan (and apply it if it is successful) to the dev environment.

If you log into your development Okta instance, you should now be able to select the profile editor and see that Terraform applied both your schema extensions.

Okta view user schema

Once you’re happy with the changes you have made to the development environment, you’ll want to promote them to the production environment. To do this, you’ll need to create a pull request from your dev branch to the master branch that controls production. As you have branch protection enabled, this provides an opportunity for others to review the changes and for any discussion to take place before you change the production configuration.

GitHub open pull request

Once you create the pull request, GitHub provides a nice UI experience that shows the branch protection in action.

GitHub review required

The status checks run plans against your two Okta instances so the reviewer can verify what the change would do. Clicking “details” on either of these will open Terraform Cloud and show the output of the plan.

Terraform Cloud add extension

In this case, the change proposed to the schema may have an unintended side effect, so the reviewer asked for a modification before applying.

GitHub comment

Next, update your file to remove required = true under the second schema extension. When you push this change to the dev branch, Terraform will run the plan again to validate the change. Once you are satisfied, the reviewer can approve the pull request and merge the change to master.

NOTE: If you are an administrator of the repository, you can force the merge through without needing someone else to review it.

GitHub comment on pull request

Now that the pull request is merged to the master branch, the production workspace will automatically run the Terraform plan again and you will be given one last chance to confirm the changes.

Terraform Cloud apply finished

And that’s all! You now have a simple multi-stage build pipeline that allows you to review and deploy changes to each of your Okta environments in a simple and reliable way. This enables you to manage your identity service just like you would any other infrastructure component.

If you enjoyed this post, you may want to follow us on Twitter, subscribe to our YouTube channel or follow us on Facebook. And if you’re interested in DevOps information, you may enjoy these other posts we’ve written: