On this page

Manage branding with Terraform

Change the appearance of Okta sign-in pages, outgoing emails, and the End-User Dashboard.


Learning outcomes

  • Import Okta objects that control color, logos, and page styling into your Terraform configuration.

  • Customize the appearance of sign-in pages, error pages, and the Okta End-User Dashboard, including color, logos, and page styling.

  • Customize email template content and appearance.

What you need

Overview

The Okta Brands feature enables you to customize parts of your org's end-user experience. For example, you can customize the colors and images on your Okta sign-in pages, error pages, and End-User Dashboard. You can customize email templates in multiple languages and create custom email domains for outgoing Okta emails. For general information, see the Brands concept article.

Configure the default brand or add a brand

You can use Terraform to customize the default brand of an Okta org. You may be able to customize multiple brands if that's supported by your org.

Check if your org supports multibrand customization

Confirm that your Okta org supports multibrand customization:

  1. Go to the Admin Console for your Okta org.

  2. In the left navigation, click Security.

  3. Review the items under Security.

    • If it includes Brands, your org supports multibrand customization.

    • If it includes Branding, your org doesn't support multibrand customization. Contact Okta Support about feature availability.

Set up your configuration

Consider using a separate file in your configuration for code related to branding. For example, you could create a Terraform file called brands.tf that contains brands and themes. For guidance on organizing your files, see Create a basic Okta Terraform configuration.

Add or confirm the API scopes

Your Terraform integration requires the appropriate scopes that depend on what you're managing:

Manage Required Scope
Brands and themes okta.brands.manage
Customize email templates okta.templates.manage
Add custom email domains okta.domains.manage
Add custom email domains okta.emailDomains.manage

To grant scopes in the Admin Console and to include them in your Terraform code, see the articles on enabling your API service app for Terraform access and Create a basic Okta Terraform configuration.

Create or import the brand resource

There are two main customization resources in the Okta Terraform provider:

  • Brand: Some settings and defaults for a custom brand. Each brand is associated with a custom domain. For general brand information, see the Brands concept article. For information on creating a custom domain, see Manage custom domains with Terraform.

  • Theme: Settings that change the appearance of the Okta Sign-In Widget, such as the logo, background image, colors, and how to apply the colors. Each brand has exactly one theme, which is created when Okta creates the brand resource.

The instructions for using these resources in Terraform differ if your org type supports multibrand customization.

  • Okta recommends creating custom domains first, and then new brand resources if your org supports multibrand customization. However, you can modify the default brand to support app integrations that don't use a custom domain.

  • You must modify the default brand if your org doesn't support multibrand customization.

Create a new brand (requires multibrand customization)

  1. Create new custom domains as described in Manage custom domains with Terraform.

  2. Create a brand resource, for example:

    resource "okta_brand" "A" {
      name = "A"
      agree_to_custom_privacy_policy = true
      custom_privacy_policy_url = "http://a.com/privacy"
    }
    
  3. Run a terraform apply command. To apply only the new resource at this time, run a targeted apply command:

    terraform apply -target okta_brand.A
    
  4. In your domain definition, set the brand_id to the ID of your new brand:

    resource "okta_domain" "A" {
      name = "a.example.com"
      certificate_source_type = "OKTA_MANAGED"
      brand_id = okta_brand.A.id
    }
    
  5. Due to how the core Okta API works, Terraform resources don't directly create the theme object. Instead, your Terraform code gets the theme from the already-created brand object. Your new brand automatically creates an associated theme object on the server. Modifying the theme object from Terraform requires using the Terraform feature called import (opens new window). The import process allows you to get the theme and change its one theme in place.

    There are several ways to import objects. The most common ways are the terraform import CLI and the import block syntax (requires Terraform 1.5 or later). The following example shows how to use Terraform import block syntax to get the theme. It looks up the theme based on the associated brand, passing the ID to the import command in the format "<brand-id>/<theme-id>". In your theme object, include the required fields with _variant suffix, as shown in the example.

    import {
      to = okta_theme.theme_A
      id = "${okta_brand.A.id}/${tolist(data.okta_themes.list_of_themes_A.themes)[0].id}"
    }
    
  6. Create a Terraform resource that gets a list of themes for a brand. It takes a brand ID and returns a list of themes. A brand just contains one theme, so its array of themes always has exactly one element.

    data "okta_themes" "list_of_themes_A" {
      brand_id = okta_brand.A.id
    }
    
  7. Create a new okta_theme resource:

    1. Set the brand_id to the ID of your brand.

    2. Set the theme ID using this syntax, which uses the data source that you created in the previous step:

      import {
        to = okta_brand.default_brand
        id = "${data.okta_brand.data_default_brand.id}"
      }
      
    3. Set the required arguments for colors. Set primary_color_hex and secondary_color_hex to the hex values of the desired colors for your org's primary and secondary colors.

    4. Set the required arguments for visual variants. For now, set sign_in_page_touch_point_variant, end_user_dashboard_touch_point_variant, error_page_touch_point_variant, and email_template_touch_point_variant to "OKTA_DEFAULT". See the okta_theme (opens new window) resource for the definitions and other variants that you can use.

The following example shows the code for the domain, the import, and the theme declaration:

resource "okta_domain" "A" {
  name = "a.example.com"
  certificate_source_type = "OKTA_MANAGED"
  brand_id = okta_brand.A.id
}

# Create a list of themes for our brand.
# In practice, this list contains exactly one theme.
data "okta_themes" "list_of_themes_A" {
  brand_id = okta_brand.A.id
}

import {
  to = okta_theme.theme_A
  id = "${okta_brand.A.id}/${tolist(data.okta_themes.list_of_themes_A.themes)[0].id}"
}

resource "okta_theme" "theme_A" {
  brand_id = okta_brand.A.id
  theme_id = tolist(data.okta_themes.list_of_themes_A.themes)[0].id

  primary_color_hex    = "#880808"
  secondary_color_hex  = "#880808"

  sign_in_page_touch_point_variant       = "OKTA_DEFAULT"
  end_user_dashboard_touch_point_variant = "OKTA_DEFAULT"
  error_page_touch_point_variant         = "OKTA_DEFAULT"
  email_template_touch_point_variant     = "OKTA_DEFAULT"
}

Modify the default brand (for all org types)

This section is for orgs that don't support multibrand customization.

Use Terraform import blocks to import Okta objects and automatically generate the corresponding resources in your Terraform configuration.

  1. In your main.tf Terraform configuration file, add an okta_brands data source. This data source gets a list of existing brands in your org.

    data "okta_brands" "all_brands" {}
    
  2. Import the default brand. Terraform doesn't directly create the default brand object. Use the Terraform import block to get it from the associated brand on the server.

    import {
      to = okta_brand.default_brand
      id = "${data.okta_brand.data_default_brand.id}"
    }
    
  3. Declare the brand resource imported in the previous step. The name field is the only required field. If you set this to something other than its current name, Okta changes the visible name of the default brand on the server.

    resource "okta_brand" "default_brand" {
    name = "default_brand"
    }
    
  4. Add an okta_brand data source to get the ID of the default brand. For single-brand customization, use the first brand in the array.

    data "okta_brand" "data_default_brand" {
      brand_id = tolist(data.okta_brands.all_brands.brands)[0].id
    }
    
  5. Import the default theme. Terraform doesn't directly create the default theme object. Use Terraform import to get it from the associated brand on the server.

    import {
      to = okta_theme.theme_default
      id = "${data.okta_brand.data_default_brand.id}/${tolist(data.okta_themes.data_list_themes_for_default_brand.themes)[0].id}"
    }
    
  6. Declare the theme resource imported in the previous step. The following shows how to set up the brand and theme IDs. All fields shown in this example are required:

    • Set primary_color_hex and secondary_color_hex to the hex values of the colors that you want to use for your org (the example for this step shows the default values).

    • Set sign_in_page_touch_point_variant, end_user_dashboard_touch_point_variant, error_page_touch_point_variant, and email_template_touch_point_variant to "OKTA_DEFAULT". See the okta_theme (opens new window) resource for the definitions of other user interface customizations.

      resource "okta_theme" "theme_default" {
        brand_id = data.okta_brand.data_default_brand.id
        theme_id = tolist(data.okta_themes.data_list_themes_for_default_brand.themes)[0].id
        primary_color_hex    = "#1662dd"
        secondary_color_hex  = "#ebebed"
        sign_in_page_touch_point_variant = "OKTA_DEFAULT"
        end_user_dashboard_touch_point_variant ="OKTA_DEFAULT"
        error_page_touch_point_variant = "OKTA_DEFAULT"
        email_template_touch_point_variant = "OKTA_DEFAULT"
      }
      
  7. Run terraform apply.

For additional customization with optional arguments, see the following sections.

Customize optional brand settings

You can customize the appearance and behavior of the site using optional brand arguments. These include the locale, privacy policy, removing the text "Powered by Okta", and setting the default Okta app integration.

Set the language

To change the language for a brand, set the locale argument to the BCP 47 format (opens new window) for the locale. The following example sets the language to French.

resource "okta_brand" "name_of_brand" {
  name = "name_of_brand
  locale = "fr"
}

Set the privacy policy

To use a custom privacy policy in the footer of your Okta pages for a brand, set agree_to_custom_privacy_policy to true and custom_privacy_policy_url. The following example sets a custom privacy policy.

resource "okta_brand" "name_of_brand" {
  name = "name_of_brand"
  agree_to_custom_privacy_policy = true
  custom_privacy_policy_url = "https://example.com/policy.html"
}

Remove "Powered by Okta"

To remove "Powered by Okta" from the sign-in page, set remove_powered_by_okta to true.

resource "okta_brand" "name_of_brand" {
  name = "name_of_brand"
  remove_powered_by_okta = true
}

Set the default app integration

Set the default brand by setting default_app_app_instance_id to an Okta app instance ID. This enables you to customize the app name that appears in any links. Set the default_app_app_link_name to the customer-facing name for the app integration.

resource "okta_brand" "name_of_brand" {
  name = "name_of_brand"
  default_app_app_instance_id = okta_app_oauth.my_oauth_app.id
  default_app_app_link_name = "Corporate mail"
}

Customize optional theme settings

Change the colors, logo, and background image of your sign-in page, End-User Dashboard, and error pages.

Set logos and background images

You can customize the brand images shown in the table. Set the value of the theme attribute shown in the table to the URL of the desired local image.

Image type Attribute
Authentication dialog logo logo
Favorite icon (favicon) favicon
Background image background_image

For example, add this configuration code to customize all three attributes:

resource "okta_theme" "image_example" {
  logo                     = "{URL-for-logo-image}"
  favicon                  = "{URL-for-favicon-image}"
  favbackground_image      = "{URL-for-background-image}"
}

Set contrast colors

You set the primary color settings in the theme resource in the required fields primary_color_hex and secondary_color_hex.

You can also set the contrast colors. For the primary contrast, set the color in the argument primary_color_contrast_hex. For the secondary color contrast, set secondary_color_contrast_hex.

resource "okta_theme" "color_contrast_example" {
  primary_color_contrast_hex                     = "0x121212"
  secondary_color_contrast_hex                   = "0x575757"
}

For more information about how Okta uses colors and the customizations in the color variant option fields, see the provider for the theme resource (opens new window).

Customize an email template

Use the okta_email_customization resource to create customized email templates. Specify the type of email in the template_name attribute, such as "ForgotPassword", for the email sent to reset a forgotten password. See the Okta Terraform provider documentation (opens new window) for the list of supported email templates.

You can also provide multiple template versions for different languages by setting the language attribute to the desired locale.

  1. Add the required template management scope if it's not already granted. Do this by adding the okta.templates.manage scope to your configuration and to your org's Terraform service app. For more information on adding scopes in Okta, see Add or confirm the API scopes.

  2. Create a new okta_email_customization resource.

  3. Set its brand_id field to the ID of your brand.

    brand_id      = okta_brand.A.id
    
  4. Set the is_default attribute if you create multiple languages for the same template. Set is_default to true in the template for the default language. Set it to false for all other language versions of the same template. Also add a depends_on attribute set to the default template for the language. You must create the default template first.

    The following code shows two language versions of the template resource for the forgot password email. The first is the default version in French. The second is in Spanish and depends on the default French version.

      resource "okta_email_customization" "forgot_password_en" {
        brand_id      = tolist(data.okta_brands.test.brands)[0].id
        template_name = "ForgotPassword"
        language      = "fr"
        is_default    = true
        subject       = "Account password reset"
        body          = "Salut $$user.firstName,<br/><br/>Cliquez sur ce lien pour réinitialiser votre mot de passe: $$resetPasswordLink"
      }
    
      resource "okta_email_customization" "forgot_password_es" {
        brand_id      = tolist(data.okta_brands.test.brands)[0].id
        template_name = "ForgotPassword"
        language      = "es"
        subject       = "Restablecimiento de contraseña de cuenta"
        body          = "Hola $$user.firstName,<br/><br/>Haga clic en este enlace para restablecer tu contraseña: $$resetPasswordLink"
    
        depends_on = [
          okta_email_customization.forgot_password_en
        ]
      }
    
  5. Set the subject and body fields to the subject and body text.

Use two dollar signs ($$) before any Okta variables in the HTML to escape the string interpolation in your configuration file—for example, $${brand.theme.primaryColor}. Variables with only one dollar sign $, are interpolated by Terraform. See the provider documentation (opens new window) for the complete list of Okta variables that you can use.

Some types of templates include required variables. For example, the reset password template must contain $$resetPasswordLink or $$oneTimePassword. For the list of variables for each template name, see Velocity variables (opens new window).

Use HTML in an email template

You can use HTML in your email templates. Use the HashiCorp heredoc syntax to use multiline HTML. The initial HTMLDELIMITER in the example body text is an arbitrary delimiter string that you define. See Heredoc Strings (opens new window) for the syntax.

For example:

resource "okta_email_customization" "user_activation_en" {
  brand_id      = okta_brand.A.id
  template_name = "UserActivation"
  language = "en"
  is_default = true
  subject = "Activate your new Okta account!"
  body = <<HTMLDELIMITER
  <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
  <html>
  <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
  <div style="background-color: $${brand.theme.secondaryColor}; margin: 0">
  <table style="font-family: 'Proxima Nova', 'Century Gothic', Arial, Verdana, sans-serif; font-size: 14px; color: #5e5e5e; width:98%; max-width: 600px; float: none; margin: 0 auto;" border="0" cellpadding="0" cellspacing="0" valign="top" align="left">
    <tr align="middle"><td style="padding-top: 30px; padding-bottom: 32px;"><img src="$${brand.theme.logo}" height="37"></td></tr>
    <tr bgcolor="#ffffff"><td>
      <table bgcolor="#ffffff" style="width: 100%; line-height: 20px; padding: 32px; border: 1px solid; border-color: #f0f0f0;" cellpadding="0">
          <tr>
              <td style="color: #5e5e5e; font-size: 22px; line-height: 22px;">
              $${org.name} - Welcome to Okta!
              </td>
          </tr>
          <tr>
              <td style="padding-top: 24px; vertical-align: bottom;">
                  Hi $!{StringTool.escapeHtml($!{user.profile.firstName})},
              </td>
          </tr>
          <tr>
              <td style="padding-top: 24px">
                  Your organization is using Okta to manage your web applications. You can access all your applications through one secure home page. Watch this short video to learn more: <a href="https://www.okta.com/intro-to-okta/" style="color: #007dc1; text-decoration: none;"><span style="color: #007dc1; text-decoration: none;">https://www.okta.com/intro-to-okta/</span></a>
              </td>
          </tr>
          <tr>
              <td style="padding-top: 24px;">
                  Your system administrator has created an Okta user account for you.<br/>
                  <strong>Click the following link to activate your Okta account:</strong>
              </td>
          </tr>
          <tr>
              <td align="center">
                  <table border="0" cellpadding="0" cellspacing="0" valign="top">
                    <tr>
                     <td align="center" style="height: 39px; padding-top: 24px; padding-bottom: 8px;"><a id="reset-password-link" href="$$activationLink" style="text-decoration: none;"><span style="display: block; padding: 9px 32px 7px 31px; border: 1px solid; text-align: center; cursor: pointer; color: #fff; border-radius: 3px; background-color: $${brand.theme.primaryColor}; border-color: $${brand.theme.primaryColor}; box-shadow: $${brand.theme.primaryColor} 0 1px 0;">Reset Okta Account</span></a>
                          </td>
                      </tr>
                      <tr>
                          <td align="center" style="color: #999;">
                              This link expires in $${f.formatTimeDiffHoursNowInUserLocale($${org.activationTokenExpirationHours})}.
                          </td>
                      </tr>
                  </table>
              </td>
          </tr>
          <tr>
              <td style="padding-top: 24px;">
                  Your username is <strong>$${user.profile.login}</strong><br/>
                  Your organization's sign-in page is <a href="$${baseURL}" style="color: #007dc1; text-decoration: none;"><span style="color: #007dc1; text-decoration: none;">$${baseURL}</span></a>
              </td>
          </tr>
          <tr>
              <td style="padding-top: 24px;">
                  If you experience difficulties accessing your account, send a help request to your system administrator using the link: <a href="$${baseURL}/help/login" style="color: #007dc1; text-decoration: none;"><span style="color: #007dc1; text-decoration: none;">$${baseURL}/help/login</span></a>
              </td>
          </tr>
      </table>
    </td></tr>
    <tr>
      <td>
          <table style="width: 100%; line-height: 20px; padding: 32px;" cellpadding="0">
              <tr>
                  <td style="font-size: 12px; padding-top: 24px; color: #999;">
                      This email was automatically sent using <a href="https://www.okta.com" style="color:#616161">Okta's service</a>. Replies are not monitored or answered. Okta has can't see who receives these emails, and is not responsible for, and disclaims any and all liability associated with, this email's content. <br/><br/>The sender may have customized the contents of the email. If you would like to notify Okta of anything suspicious, report it to security@okta.com.
                  </td>
              </tr>
          </table>
      </td>
    </tr>
  </table>
  </div>
  </body>
  </html>
  HTMLDELIMITER
}

The following image shows an example of an email generated using the template:

An activation email with personal information and URLs hidden by gray rectangles.

Create a custom email domain

Use a custom domain for emails sent from Okta instead of the default email address of noreply@okta.com.

Okta polls your custom domain once daily to confirm its operational status. If the poll fails, Okta alerts the super administrators by email. It also starts using noreply@okta.com until the issue is resolved.

There are two limitations to email domain customization:

  • Okta can't send an email from a domain that uses SendGrid. (opens new window) You can configure a subdomain with your DNS provider for custom Okta emails.

  • There's a maximum of 10 DNS lookups in an SPF record.

Okta recommends that your domain implement the Sender Policy Framework (SPF) (opens new window) to prevent sender address forgery. If you have already implemented SPF in your custom domain, update the SPF record to include Okta mail servers.

Add an email domain in Terraform

Creating and adding a custom email domain takes several steps that you must complete in order. At least one of the steps may require waiting for up to a day:

  1. Create an email domain resource.
  2. Update the domain with the CNAME and TXT records.
  3. Request email domain verification.
  4. Test your email domain.

Okta sends a confirmation email to your super administrators after your custom email domain is configured and working.

Create an email domain resource

  1. Add an email_domain (opens new window) resource.

  2. Set the brand_id to your brand's ID. For multibrand customization, set the brand_id argument to the ID for the brand to associate it with the new email domain.

    data "okta_brands" "test" {
    }
    
    resource "okta_email_domain" "mymail" {
      brand_id     = tolist(data.okta_brands.test.brands)[0].id
      domain       = "mail.example.com"
      display_name = "mymail"
      user_name    = "admin"
    }
    
  3. Use terraform plan and terraform apply to deploy the custom domain to Okta.

    Note: The Okta org must verify that you control the DNS records for this domain before you can use it in your Terraform configuration. Follow the steps in Update the domain with the CNAME and TXT records, Request email domain verification, and Test your email domain to verify the email domain.

  4. Add this output block to print the set of DNS records required for the domain.

    output "email_dns_records_to_update_company1" {
      value = okta_email_domain.mymail.dns_validation_records
    }
    
  5. Run terraform apply.

  6. Run terraform output -json {outputVariableName} to view the configuration information in JSON format. Okta recommends saving the output to a file for your records. For example, the following is the command line for an output variable of email_dns_records_to_update_company1:

    terraform output -json email_dns_records_to_update_company1 > email_dns_records.json
    
  7. Review the JSON file for the required content: one object with a record_type attribute of TXT and three objects with a record_type attribute of CNAME. Use this output in the next section.

    The content looks similar to the following example:

    "email_dns_records_to_update_company1" = [
      {
        "expiration" = "",
        "fqdn" = "_oktaverification.mail.example.com",
        "record_type" = "TXT",
        "value" = "9da4ac4cb6ca4bb3b78934f91ed4c60b",
      },
      {
        "expiration" = "",
        "fqdn" = "mail.mail.example.com",
        "record_type" = "cname",
        "value" = "u17770251.wl002.sendgrid.net",
      },
      {
        "expiration" = "",
        "fqdn" = "p03._domainkey.mail.example.com",
        "record_type" = "cname",
        "value" = "p03.domainkey.u17770251.wl002.sendgrid.net",
      },
      {
        "expiration" = "",
        "fqdn" = "p032._domainkey.mail.example.com",
        "record_type" = "cname",
        "value" = "p032.domainkey.u17770251.wl002.sendgrid.net",
      },
    ]
    

Update the domain with the CNAME and TXT records

This step requires adding DNS records to the custom domain using your domains' service provider. This usually requires administrative access. If you don't have this access, contact your system administrator.

The specific user interface or commands for adding the CNAME and TXT records depends on your service provider. Some may include automation using Terraform.

The following are the generic steps to add the records. The specific varies.

  1. Locate the JSON output from Create an email domain resource.

  2. Sign in to your domain provider for your custom domain and navigate to the domain record editor.

  3. Create three CNAME records, one for each of the three objects in the JSON output. The values you need for this step are in the object in the JSON output with a record_type of "CNAME".

    1. Set the fully qualified domain name (FQDN) of the CNAME record to the value of the fqdn field in the JSON output.

    2. Set the value of the CNAME record to the string in the values array of the JSON output. This is the name for your custom domain, such as example-org.customdomains.oktapreview.com.

    3. Save the new record in your ISP's control panel for DNS records.

  4. Create a TXT record to support Okta domain verification. The values you need for this step are in the object in the JSON output with a record_type of "TXT".

    1. Set the fully qualified domain name (FQDN) of the TXT record to the value of the fqdn field in the JSON output. Include any underscores that are at the start of the value.

    2. Set the value of the TXT record to the string in the values array of the JSON output. In the previous example of a JSON record, this value is 9da4ac4cb6ca4bb3b78934f91ed4c60b.

    3. Save the new record in your ISP's control panel for DNS records.

  5. Add the SPF record to your DNS zone (the root domain). An SPF record specifies the mail servers authorized by your org to send mail from your domain. If your root domain already has an SPF record, the following update can prevent spoofers from sending mail that mimics your domain. For example, if you only send mail from Microsoft Office 365, your SPF record has an include statement similar to this example:

    example.com TXT    v=spf1 include:spf.protection.outlook.com -all
    

    You must add another include statement that specifies your email domain. In the previous example, that would be mail.example.com. If you combine the new include statement with the previous SPF example, it looks similar to this example:

    example.com TXT    v=spf1 include:mail.example.com include:spf.protection.outlook.com -all
    
  6. Wait until the DNS records propagate. This step may take up to 24 hours, though could take as little as 15 minutes. Check if your DNS records are available using a tool, such as Dig (opens new window).

Request email domain verification

Request an email domain verification after the DNS changes have propagated. Okta confirms that the TXT records for your domain contain the Okta-generated value proving that you control the domain.

  1. Add a new email domain verification resource that references your email_domain_id object:

    resource "okta_email_domain_verification" "example" {
      email_domain_id = okta_email_domain.mymail.id
    }```
    
    
  2. Run terraform apply.

Wait up to 15 minutes for the Okta server to verify the DNS changes. If the terraform apply command fails wait a few minutes and try again.

Test your email domain

Test your email domain integration by triggering an Okta email. See the reference of situations that cause emails to be sent (opens new window).

If everything works correctly, consider commenting out the output block that extracts the set of DNS records that require changing.

Error page content can't be changed in Terraform

Although the color/image use for error pages is customizable in the brand, the actual content of error pages is unavailable to customize using Terraform.