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
Familiarity with the Terraform terms: configuration, resources, state, and commands. See the Terraform documentation introductory concepts (opens new window). Also, see the introduction page for Okta Terraform automation.
An Okta org
A Terraform configuration that can access your Okta org
An Okta user account with the super administrator role
The ability to update the DNS records of your public custom domain to configure a custom email address from which Okta can send mail
Terraform 1.8.5 or later
Okta Terraform provider 4.9.1 or later
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:
Go to the Admin Console for your Okta org.
In the left navigation, click Security.
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)
Create new custom domains as described in Manage custom domains with Terraform.
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" }
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
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 }
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 theimport
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}" }
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 }
Create a new
okta_theme
resource:Set the
brand_id
to the ID of your brand.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}" }
Set the required arguments for colors. Set
primary_color_hex
andsecondary_color_hex
to the hex values of the desired colors for your org's primary and secondary colors.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
, andemail_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.
In your
main.tf
Terraform configuration file, add anokta_brands
data source. This data source gets a list of existing brands in your org.data "okta_brands" "all_brands" {}
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}" }
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" }
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 }
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}" }
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
andsecondary_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
, andemail_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" }
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.
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.Create a new
okta_email_customization
resource.Set its
brand_id
field to the ID of your brand.brand_id = okta_brand.A.id
Set the
is_default
attribute if you create multiple languages for the same template. Setis_default
totrue
in the template for the default language. Set it tofalse
for all other language versions of the same template. Also add adepends_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 ] }
Set the
subject
andbody
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:
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:
- Create an email domain resource.
- Update the domain with the
CNAME
andTXT
records. - Request email domain verification.
- 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
Add an email_domain (opens new window) resource.
Set the
brand_id
to your brand's ID. For multibrand customization, set thebrand_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" }
Use
terraform plan
andterraform 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
andTXT
records, Request email domain verification, and Test your email domain to verify the email domain.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 }
Run
terraform apply
.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 ofemail_dns_records_to_update_company1
:terraform output -json email_dns_records_to_update_company1 > email_dns_records.json
Review the JSON file for the required content: one object with a
record_type
attribute ofTXT
and three objects with arecord_type
attribute ofCNAME
. 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.
Locate the JSON output from Create an email domain resource.
Sign in to your domain provider for your custom domain and navigate to the domain record editor.
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 arecord_type
of"CNAME"
.Set the fully qualified domain name (FQDN) of the
CNAME
record to the value of thefqdn
field in the JSON output.Set the value of the
CNAME
record to the string in thevalues
array of the JSON output. This is the name for your custom domain, such asexample-org.customdomains.oktapreview.com
.Save the new record in your ISP's control panel for DNS records.
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 arecord_type
of"TXT"
.Set the fully qualified domain name (FQDN) of the
TXT
record to the value of thefqdn
field in the JSON output. Include any underscores that are at the start of the value.Set the value of the
TXT
record to the string in thevalues
array of the JSON output. In the previous example of a JSON record, this value is9da4ac4cb6ca4bb3b78934f91ed4c60b
.Save the new record in your ISP's control panel for DNS records.
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
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.
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 }```
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.