Deploy OpenSearch UI with Terraform

In this tutorial you will use Terraform to provision an Amazon OpenSearch domain, create an OpenSearch UI Application, and automatically set up a sample dashboard — all from a single terraform apply command.

By the end you will have a working OpenSearch UI Application with a pre-built dashboard you can open in your browser.

Prerequisites

Before you start, make sure you have:

  • Terraform >= 1.5 installed — Install Terraform 
  • AWS CLI v2 configured with credentials — Install AWS CLI 
  • An AWS account with permissions to create OpenSearch, Lambda, and IAM resources
  • Basic familiarity with the terminal (you don't need prior Terraform experience)

Architecture Overview

The Terraform configuration creates the following resources:

OpenSearch Domain
  └──▶ OpenSearch UI Application (managed dashboards)
         └──▶ IAM Role + Policy
                └──▶ Lambda Function (dashboard automation)
                       └──▶ Lambda Invocation (runs automatically after deploy)

Optionally, you can also enable:

  • VPC access — restricts the application to a private network
  • IAM Identity Center — enables SSO authentication

Here is what each piece does:

ResourceWhat it does
OpenSearch DomainManaged search and analytics engine that stores your data
OpenSearch UI ApplicationProvides a hosted dashboards experience on top of the domain
IAM RoleGrants the Lambda function permission to call OpenSearch APIs
Lambda FunctionCreates a workspace, ingests sample data, and builds a dashboard
Lambda InvocationTriggers the Lambda automatically after everything is provisioned

Clone the Sample Repo

The full Terraform code lives in the companion repository. Clone it and navigate to the terraform/ directory:

git clone https://github.com/aws-samples/sample-automate-opensearch-ui-dashboards-deployment.git
cd sample-automate-opensearch-ui-dashboards-deployment/terraform

The directory structure looks like this:

terraform/
├── main.tf                    # Core resources (domain, app, IAM, Lambda)
├── variables.tf               # Input variables with defaults
├── outputs.tf                 # Values printed after deployment
├── vpc.tf                     # Optional VPC resources
├── idc.tf                     # Optional IDC resources
├── terraform.tfvars.example   # Sample variable values
├── README.md                  # Reference documentation
└── lambda/
    ├── index.py               # Python handler for dashboard setup
    └── dashboards/
        └── sample.ndjson      # Sample dashboard definition

Configure Variables

Copy the example variables file:

cp terraform.tfvars.example terraform.tfvars

Open terraform.tfvars in your editor. The defaults work out of the box, but you can customize them:

# AWS region for all resources
region = "us-east-1"
 
# OpenSearch domain settings
domain_name     = "opensearch-ui-demo"
engine_version  = "OpenSearch_2.17"
instance_type   = "r6g.large.search"
instance_count  = 1
ebs_volume_size = 100
 
# OpenSearch UI Application name
app_name = "opensearch-ui-app"

All 12 variables have sensible defaults, so you can skip this step entirely if you just want to try it out.

Deploy

Deployment is a three-step process: init, plan, apply.

Step 1 — Initialize Terraform

terraform init

This downloads the required providers (AWS, AWSCC, Archive, Null) and sets up the working directory. You should see Terraform has been successfully initialized! at the end.

Step 2 — Preview the plan

terraform plan

Terraform shows you every resource it will create — without actually creating anything. Review the output to confirm it looks right. You should see resources like aws_opensearch_domain.this, awscc_opensearch_application.this, and aws_lambda_function.dashboard_automation.

Step 3 — Apply

terraform apply

Terraform asks you to confirm. Type yes and press Enter.

Now sit back — the OpenSearch domain takes about 15–20 minutes to create. After the domain is ready, Terraform creates the UI Application, deploys the Lambda function, and invokes it to set up your sample dashboard.

Here is a simplified look at the key resources Terraform creates. (See the full code in the companion repo .)

OpenSearch Domain — the data engine:

resource "aws_opensearch_domain" "this" {
  domain_name    = var.domain_name
  engine_version = var.engine_version
 
  cluster_config {
    instance_type  = var.instance_type
    instance_count = var.instance_count
  }
 
  ebs_options {
    ebs_enabled = true
    volume_size = var.ebs_volume_size
    volume_type = "gp3"
  }
 
  encrypt_at_rest       { enabled = true }
  node_to_node_encryption { enabled = true }
}

This creates a single-node OpenSearch cluster with encryption enabled. The var.* references pull values from your terraform.tfvars file.

OpenSearch UI Application — the dashboards layer:

resource "awscc_opensearch_application" "this" {
  name = var.app_name
 
  data_sources = [{
    data_source_arn         = aws_opensearch_domain.this.arn
    data_source_description = "Primary OpenSearch Domain"
  }]
 
  app_configs = [
    { key = "opensearchDashboards.dashboardAdmin.users",  value = "[\"*\"]" },
    { key = "opensearchDashboards.dashboardAdmin.groups", value = "[\"${aws_iam_role.lambda_role.arn}\"]" },
  ]
}

Notice the awscc_ prefix — this resource comes from the AWS Cloud Control provider because the standard AWS provider does not yet have an aws_opensearch_application resource. The data_sources block connects the application to the domain you just created.

Lambda Function — automates dashboard setup:

resource "aws_lambda_function" "dashboard_automation" {
  function_name    = "${var.domain_name}-dashboard-automation"
  role             = aws_iam_role.lambda_role.arn
  handler          = "index.handler"
  runtime          = "python3.11"
  timeout          = 300
  filename         = data.archive_file.lambda_zip.output_path
  source_code_hash = data.archive_file.lambda_zip.output_base64sha256
 
  environment {
    variables = {
      APP_ENDPOINT    = "application-${var.app_name}-..."
      DOMAIN_ENDPOINT = aws_opensearch_domain.this.endpoint
    }
  }
}

Terraform packages the lambda/ directory into a zip and deploys it. The Lambda reads the application endpoint from its environment variables and uses SigV4-signed requests to create a workspace, ingest sample data, and build a dashboard.

Post-deploy trigger — runs the Lambda automatically:

resource "aws_lambda_invocation" "setup_dashboards" {
  function_name = aws_lambda_function.dashboard_automation.function_name
  input         = jsonencode({ action = "setup_dashboards" })
 
  depends_on = [
    awscc_opensearch_application.this,
    aws_lambda_function.dashboard_automation,
  ]
}

The depends_on block ensures the Lambda only runs after both the application and the function are fully provisioned.

Verify Deployment

After terraform apply completes, you should see output like this:

Apply complete! Resources: 7 added, 0 changed, 0 destroyed.
 
Outputs:
 
app_endpoint  = "application-opensearch-ui-app-abc123.us-east-1.opensearch.amazonaws.com"
app_id        = "abc123"
domain_arn    = "arn:aws:es:us-east-1:123456789012:domain/opensearch-ui-demo"
domain_endpoint = "search-opensearch-ui-demo-xyz.us-east-1.es.amazonaws.com"

To verify everything worked:

  1. Check the outputs — Run terraform output to see the endpoints again at any time.

  2. Open the application — Copy the app_endpoint value and open it in your browser:

    https://<app_endpoint>/_dashboards

    You should see the OpenSearch Dashboards welcome page.

  3. Find the sample dashboard — Navigate to the "Sample Workspace" in the workspace selector. You should see the "Application Metrics" dashboard with an HTTP Status Code Distribution pie chart.

  4. Verify in the AWS Console — Go to the OpenSearch Service console in your region. You should see both the domain and the UI Application listed.

Optional: VPC Access

To restrict the application to a private VPC, add these variables to your terraform.tfvars:

enable_vpc = true
vpc_id     = "vpc-0123456789abcdef0"
subnet_ids = ["subnet-aaa", "subnet-bbb"]

Then run terraform apply again. This creates:

  • A security group allowing HTTPS (port 443) from the VPC CIDR
  • VPC configuration on the Lambda function
  • VPC endpoint authorization for the OpenSearch UI Application

Note: VPC endpoint authorization uses aws opensearch authorize-vpc-endpoint-access via the AWS CLI. Make sure the AWS CLI is installed on the machine running Terraform.

Optional: IDC Integration

To enable IAM Identity Center (SSO) for the application, add:

enable_idc       = true
idc_instance_arn = "arn:aws:sso:::instance/ssoins-0123456789abcdef0"

Then run terraform apply. This creates an IAM role trusted by the OpenSearch service and configures the UI Application to use your Identity Center instance for authentication.

Cleanup

When you are done, destroy all resources to stop incurring charges:

terraform destroy

Type yes when prompted. This removes the OpenSearch domain, UI Application, Lambda function, IAM roles, and all optional resources. The domain deletion takes a few minutes.

Cost note: The default r6g.large.search instance runs continuously and is the primary cost driver. Always run terraform destroy when you are finished experimenting.

Next Steps