Featured image of post What it took to get a scalable Terraform Okta Admin Module

What it took to get a scalable Terraform Okta Admin Module

Writing a Terraform Module for Okta Admin Capabilities, the hurdles, and how we achieved scalable administrator access

Introduction

At $currentJob, we wanted to allow our end-users of Okta, who are administrators in their respective platforms, to view or manage applications they are responsible for. An easy example is our Salesforce Team. We want them to manage everything related to Salesforce. But how can we do that in a way that requires limited or low administration overhead?

The initial project

When we initially started our project, we were using 4.11.0 of the Terraform Module, and in some cases, even older versions to fix some issues we had with that specific version. This was great, as it could have us do a wildcard lookup with groups, so we could extract the information we needed for a resource set.

The original plan:

  • Allow an administrator or technical owner To have read (and potentially edit access):
    • To all of the applications under their domain
    • To all of the groups under their domain
    • View
  • Allow for automatic expansion of groups and applications as they were added and as Terraform would apply

This sounds simple: We already had all the things needed to go in, and we just needed to bump up to version 4.12. Unfortunately, that’s not the case.

The Problem - Scaling Groups and Scaling App Lookups

While the okta_apps function was in the provider, it didn’t seem to do anything or work at all.

For example, the ability to use something simple:

1
2
3
4
5
6
data "okta_apps" "salesforce" {
 active_only         = true
 label_prefix        = "Salesforce"
 use_optimization    = false
 include_non_deleted = false
}

It would return errors, even though the 4.12 docs mentioned support for the label_prefix statement.

This ruined our plans.

We tried to find workarounds, but we didn’t like any of our ideas, so we just decided to wait.

Finding similarities

Unfortunately, we couldn’t find any similar issues with the actual error/problem (Terraform failing to run) or any possible similar work to see if we could find any workarounds. But @exitcode0 had the same use case as what we were trying to achieve.

So, there were obviously some positive use case situations here.

Filing Issue

This ruined our plans and needed to be announced/made others aware, so I/we ($currentJob) filed an Okta Support case and a Github Issue #2251 shortly after a very long back-and-forth about Okta Resource Sets and whether the Terraform resource was working for anyone else on Slack.

Waiting… Resolution and Forgetting

Since we couldn’t progress, we moved on to another part of the Terraform project, and life moved on.

Issues Resolved

So about a month and a half later, the issue was finally closed and resolved with a merged PR, and the module appeared to do exactly what we wanted. It added the ability to do lookups and searches on either name or filter, allowing us more flexibility with our lookup queries.

Forgetting

Life moved on to the point where I forgot what type of code we were writing in the module. That made it difficult to progress or continue forward with anything. So we ended up rewriting the whole module, as it was faster and easier to do soβ€”because what we needed had a lot, and I mean a lot, of data lookups.

A Happy Bonus

So, not only did we resolve our issues, but we also resolved three other issues in the same release, all of which pertain to ORNs and Resource Sets, making the potential for the admin module even better.

Building it out

So how did we do it?

Variables

Here’s a breakdown of the key variables that help this module function. These control what resources are targeted, what roles are assigned, and how deeply customized the setup can be.

🌎 Environment and Context

  • environment: Tells the module if you’re working in a preview or production Okta instance, but can be omitted if you would prefer to use the okta_org_metadata data.
  • admin_role_type: Picks which type of admin role to assign (built-in or custom).

πŸ‘€ Who Gets Admin Roles?

  • Group-Based Assignment

    • admin_assignment_group_names: Exact group names to assign roles to.
    • admin_assignment_group_patterns: Fuzzy patterns to dynamically match group names.
    • admin_assignment_group_ids: Assign roles to groups using known group IDs.
  • User-Based Assignment

    • admin_user_logins: Directly assign roles to specific user logins.
    • admin_user_search_patterns: Pattern-based user search (e.g., all emails starting with “admin”).

πŸ“‹ What Resources Are Managed?

  • Applications

    • managed_app_names, managed_app_labels: Target apps by exact name or label.
    • managed_app_patterns: Find apps dynamically with search patterns.
    • managed_app_ids: Use when you have the app ID directly.
  • Groups

    • managed_group_names, managed_group_patterns, managed_group_ids: Target groups by name, pattern, or ID.

πŸ”¨ Custom Roles (When admin_role_type = "CUSTOM")

  • Role Config

    • custom_role_id: Use an existing custom role.
    • custom_role_label, custom_role_description: For creating a new custom role.
    • custom_role_permissions: Fine-grained control over what the role can do, validated against a large list of supported permissions.
  • Resource Set Config

    • resource_set_name, resource_set_description: Required when creating a new resource set for custom roles.
    • resource_set_id: Optionally reuse an existing one.

πŸ“’ Notifications and Compatibility

  • admin_notifications: Control which types of admin alerts users get. Highly customizable.
  • disable_notifications: Legacy variable. Kept for backward compatibility.
  • lookup_existing_role_subscription: Enables reuse of existing role subscriptions if needed.

Data Lookups

🌐 Organization Metadata

  • data "okta_org_metadata" "current"

    • Retrieves metadata about the current Okta organization (e.g., name, subdomain, settings).

πŸ“± Applications to Be Managed by Admins

  • data "okta_app" "managed_apps_by_name"

    • Looks up applications using exact names via the label field.
    • Source: var.managed_app_names.
  • data "okta_app" "managed_apps_by_label"

    • Looks up applications using the exact label.
    • Source: var.managed_app_labels.
  • data "okta_apps" "managed_apps_by_pattern"

    • Searches for active applications whose labels match a pattern.
    • Uses the q search parameter.
    • Source: var.managed_app_patterns.
  • data "okta_app" "managed_apps_by_id"

    • Retrieves applications by their exact Okta ID.
    • Source: var.managed_app_ids.

πŸ‘₯ Groups to Receive Admin Roles

  • data "okta_groups" "admin_groups_by_pattern"

    • Searches for groups where the profile name contains a given pattern.
    • Uses search = "profile.name co \"<pattern>\"".
    • Source: var.admin_assignment_group_patterns.
  • data "okta_group" "admin_groups_by_name"

    • Looks up groups by exact name.
    • Source: var.admin_assignment_group_names.
  • data "okta_group" "admin_groups_by_id"

    • Retrieves groups by their exact Okta group ID.
    • Source: var.admin_assignment_group_ids.

πŸ‘₯ Groups to Be Managed by Admins

  • data "okta_groups" "managed_groups_by_pattern"

    • Searches for groups by pattern (profile name contains a given substring).
    • Source: var.managed_group_patterns.
  • data "okta_group" "managed_groups_by_name"

    • Looks up groups by exact name.
    • Source: var.managed_group_names.
  • data "okta_group" "managed_groups_by_id"

    • Retrieves groups by their Okta group ID.
    • Source: var.managed_group_ids.

πŸ§‘β€πŸ’» Users for Admin Role Assignments

  • data "okta_users" "admin_users"

    • Searches for users whose profile fields start with a given value.
    • Source: var.admin_user_search_patterns.
  • data "okta_users" "admin_users_by_login"

    • Searches for specific users by login using exact match.
    • Source: var.admin_user_logins.

βœ‰οΈ Role Subscription Lookup

  • data "okta_role_subscription" "existing"

    • Conditionally retrieves an existing admin notification role subscription.
    • Controlled by: var.lookup_existing_role_subscription and var.admin_role_type.

Local Blocks

Local variables are where things get stitched together. Here’s how we prepare all the pieces to work together effectively:

πŸ“š Org & Environment Setup

  • Pull in current org ID and domain from Okta metadata.
  • Decide what “partition” we’re working in β€” oktapreview or okta.

πŸ”Ή Managed Applications

  • Collect applications to be managed by admins from:

    • Names
    • Labels
    • Patterns (fuzzy search)
    • IDs
  • Merge and flatten them all into a single list (all_managed_apps).

  • Extract app IDs and convert them into API resource URIs.

πŸ”Ή Managed Groups

  • Collect groups to be managed using:

    • Exact names
    • Patterns
    • IDs
  • Build full URIs for these as well.

πŸ§‘β€πŸ’» Admin Users & Groups

  • Collect users and groups that should receive admin roles using:

    • Exact matches
    • Search patterns
    • Direct IDs
  • Build ORNs (Okta Resource Names) for each admin user and group.

✨ Role & Assignment Logic

  • Determine whether you’re using a custom or standard role.
  • If custom, use a provided ID or fallback to one created within the module.
  • Gather URI-style member entries for users/groups receiving custom roles.

πŸ“’ Notifications & Validation

  • Handle notification subscriptions differently depending on role type.
  • Include a fail-fast validation block to make sure custom roles have all required inputs.

These locals do the hard work of abstracting input formats into API-ready data, so the rest of the module can stay clean and predictable.

Administrator Roles

This is where the actual admin roles get created and assigned. Depending on whether you’re using a custom or standard role, different resources are deployed.

✨ Custom Admin Roles

  • If you choose a custom role (admin_role_type = "CUSTOM"), the module:

    • Creates a new custom admin role if a custom_role_label is provided.
    • Creates a resource set with the apps and groups that the role can manage.
    • Assigns the role to the specified users and/or groups using that resource set.

βš–οΈ Standard Admin Roles

  • If you pick a built-in Okta role like USER_ADMIN, GROUP_MEMBERSHIP_ADMIN, etc.:

    • Assigns that role directly to user IDs via okta_user_admin_roles.
    • If the role supports targeting groups (like GROUP_MEMBERSHIP_ADMIN, HELP_DESK_ADMIN, USER_ADMIN), it links those admin groups to the groups they’ll manage.
    • If the role supports app targeting (like APP_ADMIN), it links admin groups to the apps they’ll manage.

πŸ“’ Notifications

  • Uses okta_role_subscription to manage which notifications assigned admins will receive.
  • If legacy notification support is needed, the module falls back to a basic unsubscribed announcement setup.
  • Custom roles do not support notifications directly, so those blocks are skipped when custom roles are in use.

This structure makes it easy to scale from a few admins with static assignments to dynamic, role-based access at scale.

Outputs

These outputs are helpful for debugging, logging, or passing data downstream into other modules. They’re split into useful categories.

🏒 Org and Context

  • org_id, org_url, partition: Identify the Okta tenant and its environment (prod or preview).

πŸ‘¨β€πŸ’Ό Admin Role Insights

  • admin_role_type, is_standard_role, using_custom_role: Tell you what kind of role is being used.
  • custom_role_id, custom_role_permissions: Expose details when a custom role is used.

πŸ”Ή Resource Set

  • resource_set_id: ID for the resource set (apps and groups the role can manage).

πŸ“„ Managed Applications

  • managed_apps, managed_app_ids: Full details and IDs of apps admins will manage.

πŸ“„ Admin Groups (Who Gets Roles)

  • admin_groups, admin_group_ids: Group info for those receiving admin rights.

πŸ“„ Managed Groups (Who Is Managed)

  • managed_groups, managed_group_ids: Group info for those being managed by admins.

πŸ§‘β€πŸ’» Admin Users

  • admin_users, admin_user_ids: User details and IDs of people who become admins.

πŸ”Ή ORNs (Okta Resource Names)

  • admin_user_orns, admin_group_orns, managed_group_orns: Useful for audit logging and referencing identities within Okta APIs.

πŸ“Š Assignment Tracking

  • user_admin_role_assignments: Shows which roles were given to which users.
  • group_admin_role_assignments: Tracks group-based role assignments, whether app-based or group-based.
  • custom_role_assignments: Gives a summary of any custom roles that were assigned β€” including role ID, resource set ID, and member count.

Putting it all together

And then, hopefully, when I can get the chance and approval, we can publish the Terraform Module, but at least this gives you an idea the complexity and the massive scope that this does to try and pull everything in to do all you need it to.

Licensed under CC BY-NC-SA 4.0
Last updated on May 23, 2025 at 12:30 CEST
 
Thanks for stopping by!
Built with Hugo