In this tutorial you will use the AWS Cloud Development Kit (CDK) to deploy an OpenSearch domain, create an OpenSearch UI Application, and automatically set up a sample workspace with dashboards from a single cdk deploy command.
Deploy with CDK
Prerequisites
- AWS CLI v2 configured with credentials
- Node.js 18 or later
- AWS CDK v2.110.0 or later (install with
npm install -g aws-cdk) - Python 3.11 or later
- Docker (used by CDK to bundle Lambda code)
- An AWS account with admin permissions
Bootstrap CDK (one-time per account/region):
cdk bootstrap aws://YOUR_ACCOUNT_ID/us-east-1Architecture Overview
The CDK stack creates these resources in order:
- IAM Role — Lambda execution role with opensearch and es permissions
- OpenSearch Domain — single-node r6g.large with 100GB GP3
- OpenSearch UI Application — connected to the domain
- Lambda Function — Python handler for dashboard automation
- Custom Resource — triggers Lambda after deployment
The Lambda generates 50 sample HTTP metrics, ingests them, creates a workspace, and builds a pie chart dashboard.
Clone and Install
git clone https://github.com/aws-samples/sample-automate-opensearch-ui-dashboards-deployment.git
cd sample-automate-opensearch-ui-dashboards-deployment/cdk
npm installRepository structure:
cdk/
bin/app.ts # CDK app entry point
lib/dashboard-stack.ts # All AWS resource definitions
package.json # Node.js dependencies
lambda/
dashboard_automation.py # Main Lambda handler
sigv4_signer.py # SigV4 signing utility
requirements.txt # Python dependenciesPreview
npx cdk diff -c masterUserArn=arn:aws:iam::YOUR_ACCOUNT_ID:role/YOUR_ROLEShows all resources that will be created without creating them.
Deploy
npx cdk deploy -c masterUserArn=arn:aws:iam::YOUR_ACCOUNT_ID:role/YOUR_ROLEType y to confirm. Takes 20-25 minutes (domain creation is the slow part).
Outputs include the OpenSearch UI endpoint, domain endpoint, workspace ID, and IDC status.
Understanding the Code
The main file is cdk/lib/dashboard-stack.ts.
Step 1 — IAM Role (created first because the UI app references its ARN):
const dashboardRole = new iam.Role(this, 'DashboardLambdaRole', {
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
inlinePolicies: {
OpenSearchAccess: new iam.PolicyDocument({
statements: [
new iam.PolicyStatement({
actions: ['opensearch:ApplicationAccessAll'],
resources: ['*']
}),
new iam.PolicyStatement({
actions: ['es:ESHttpPost', 'es:ESHttpPut', 'es:ESHttpGet'],
resources: [`arn:aws:es:${this.region}:${this.account}:domain/*`]
})
]
})
}
});Two permission sets: opensearch:ApplicationAccessAll for UI APIs and es:ESHttp* for domain data ingestion.
Step 2 — OpenSearch Domain:
const opensearchDomain = new opensearch.Domain(this, 'OpenSearchDomain', {
version: opensearch.EngineVersion.OPENSEARCH_2_11,
capacity: { dataNodes: 1, dataNodeInstanceType: 'r6g.large.search' },
ebs: { enabled: true, volumeSize: 100, volumeType: ec2.EbsDeviceVolumeType.GP3 },
encryptionAtRest: { enabled: true },
nodeToNodeEncryption: true,
enforceHttps: true,
});Step 3 — OpenSearch UI Application:
const openSearchUI = new opensearch.CfnApplication(this, 'OpenSearchUI', {
appConfigs: [
{ key: 'opensearchDashboards.dashboardAdmin.users', value: '["*"]' },
{ key: 'opensearchDashboards.dashboardAdmin.groups', value: `["${dashboardRole.roleArn}"]` }
],
dataSources: [{ dataSourceArn: opensearchDomain.domainArn }],
name: appName
});CfnApplication maps directly to the CloudFormation AWS::OpenSearch::Application resource.
Step 4 — Lambda + Custom Resource:
const dashboardFn = new lambda.Function(this, 'DashboardSetup', {
runtime: lambda.Runtime.PYTHON_3_11,
handler: 'dashboard_automation.handler',
code: lambda.Code.fromAsset('../lambda'),
timeout: cdk.Duration.minutes(5),
role: dashboardRole
});
new cdk.CustomResource(this, 'DashboardSetupResource', {
serviceToken: provider.serviceToken,
properties: {
opensearchUIEndpoint: openSearchUIEndpoint,
domainEndpoint: opensearchDomain.domainEndpoint,
workspaceName: 'workspace-demo',
region: this.region
}
});The Custom Resource tells CloudFormation to invoke the Lambda after all infrastructure is ready.
How the Lambda Works
The handler runs these steps sequentially:
- Finds the data source by calling
GET /api/saved_objects/_find?type=data-source - Creates workspace
workspace-demoviaPOST /api/workspaces - Generates 50 sample HTTP metrics and bulk-ingests them into the domain
- Creates an index pattern for
application-metrics-* - Creates a pie chart visualization for HTTP status code distribution
- Creates a dashboard containing the pie chart
All UI API calls use SigV4 signing with service name opensearch. Domain API calls use service name es.
Verify
- Open the OpenSearch Service console
- Click OpenSearch UI (Dashboards) in the left nav
- Click your application (app-demo)
- Switch to workspace-demo
- Open the Application Metrics dashboard — you should see a pie chart
Optional: VPC Access
npx cdk deploy -c masterUserArn=YOUR_ARN -c enableVpc=trueCreates a VPC, security group, and authorizes the VPC endpoint.
Optional: IAM Identity Center
npx cdk deploy -c masterUserArn=YOUR_ARN -c idcInstanceArn=arn:aws:sso:::instance/ssoins-YOUR_IDCreates an IAM role for IDC and configures the app for SSO.
Troubleshooting
| Issue | Fix |
|---|---|
| Data source not found | Retry deploy — timing issue with data source registration |
| ExpiredToken | Refresh credentials, then redeploy |
| Docker not found | Install Docker or use --no-bundling |
| 403 on presigned URL | Use opensearch as SigV4 service name, not es |
Cleanup
npx cdk destroyType y to confirm. Removes all resources. The r6g.large instance is the main cost — always destroy when done.
Next Steps
- Source code: aws-samples/sample-automate-opensearch-ui-dashboards-deployment
- Try the Terraform version: Deploy with Terraform
- Learn about Workspaces