The aws:kendra/index:Index resource, part of the Pulumi AWS provider, provisions an Amazon Kendra index that stores searchable documents and their metadata. This guide focuses on three capabilities: basic index creation with edition selection, capacity scaling for query and storage, and encryption and access control configuration.
Kendra indexes require IAM roles for CloudWatch and S3 access, and may reference KMS keys or AWS SSO for encryption and access control. The examples are intentionally small. Combine them with your own data sources, document ingestion pipelines, and application authentication.
Create a basic index with name and IAM role
Most deployments start by creating an index with a name, edition, and IAM role for CloudWatch and S3 permissions.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.kendra.Index("example", {
name: "example",
description: "example",
edition: "DEVELOPER_EDITION",
roleArn: _this.arn,
tags: {
Key1: "Value1",
},
});
import pulumi
import pulumi_aws as aws
example = aws.kendra.Index("example",
name="example",
description="example",
edition="DEVELOPER_EDITION",
role_arn=this["arn"],
tags={
"Key1": "Value1",
})
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/kendra"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := kendra.NewIndex(ctx, "example", &kendra.IndexArgs{
Name: pulumi.String("example"),
Description: pulumi.String("example"),
Edition: pulumi.String("DEVELOPER_EDITION"),
RoleArn: pulumi.Any(this.Arn),
Tags: pulumi.StringMap{
"Key1": pulumi.String("Value1"),
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var example = new Aws.Kendra.Index("example", new()
{
Name = "example",
Description = "example",
Edition = "DEVELOPER_EDITION",
RoleArn = @this.Arn,
Tags =
{
{ "Key1", "Value1" },
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.kendra.Index;
import com.pulumi.aws.kendra.IndexArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var example = new Index("example", IndexArgs.builder()
.name("example")
.description("example")
.edition("DEVELOPER_EDITION")
.roleArn(this_.arn())
.tags(Map.of("Key1", "Value1"))
.build());
}
}
resources:
example:
type: aws:kendra:Index
properties:
name: example
description: example
edition: DEVELOPER_EDITION
roleArn: ${this.arn}
tags:
Key1: Value1
The name property identifies your index. The edition property determines capacity and features: use DEVELOPER_EDITION for testing, ENTERPRISE_EDITION for production, or GEN_AI_ENTERPRISE_EDITION for generative AI applications. Once set, the edition cannot be changed. The roleArn grants Kendra permissions to write CloudWatch logs and read documents from S3 during ingestion.
Scale query and storage capacity with capacity units
As query volume or document count grows, you can add capacity beyond the base allocation.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.kendra.Index("example", {
name: "example",
edition: "DEVELOPER_EDITION",
roleArn: _this.arn,
capacityUnits: {
queryCapacityUnits: 2,
storageCapacityUnits: 2,
},
});
import pulumi
import pulumi_aws as aws
example = aws.kendra.Index("example",
name="example",
edition="DEVELOPER_EDITION",
role_arn=this["arn"],
capacity_units={
"query_capacity_units": 2,
"storage_capacity_units": 2,
})
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/kendra"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := kendra.NewIndex(ctx, "example", &kendra.IndexArgs{
Name: pulumi.String("example"),
Edition: pulumi.String("DEVELOPER_EDITION"),
RoleArn: pulumi.Any(this.Arn),
CapacityUnits: &kendra.IndexCapacityUnitsArgs{
QueryCapacityUnits: pulumi.Int(2),
StorageCapacityUnits: pulumi.Int(2),
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var example = new Aws.Kendra.Index("example", new()
{
Name = "example",
Edition = "DEVELOPER_EDITION",
RoleArn = @this.Arn,
CapacityUnits = new Aws.Kendra.Inputs.IndexCapacityUnitsArgs
{
QueryCapacityUnits = 2,
StorageCapacityUnits = 2,
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.kendra.Index;
import com.pulumi.aws.kendra.IndexArgs;
import com.pulumi.aws.kendra.inputs.IndexCapacityUnitsArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var example = new Index("example", IndexArgs.builder()
.name("example")
.edition("DEVELOPER_EDITION")
.roleArn(this_.arn())
.capacityUnits(IndexCapacityUnitsArgs.builder()
.queryCapacityUnits(2)
.storageCapacityUnits(2)
.build())
.build());
}
}
resources:
example:
type: aws:kendra:Index
properties:
name: example
edition: DEVELOPER_EDITION
roleArn: ${this.arn}
capacityUnits:
queryCapacityUnits: 2
storageCapacityUnits: 2
The capacityUnits block controls scaling. The queryCapacityUnits property increases concurrent query throughput; storageCapacityUnits adds document storage capacity. Each unit provides incremental capacity; consult AWS documentation for specific limits per edition.
Encrypt indexed data with a KMS key
Organizations with compliance requirements can encrypt indexed content using customer-managed KMS keys.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.kendra.Index("example", {
name: "example",
roleArn: thisAwsIamRole.arn,
serverSideEncryptionConfiguration: {
kmsKeyId: _this.arn,
},
});
import pulumi
import pulumi_aws as aws
example = aws.kendra.Index("example",
name="example",
role_arn=this_aws_iam_role["arn"],
server_side_encryption_configuration={
"kms_key_id": this["arn"],
})
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/kendra"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := kendra.NewIndex(ctx, "example", &kendra.IndexArgs{
Name: pulumi.String("example"),
RoleArn: pulumi.Any(thisAwsIamRole.Arn),
ServerSideEncryptionConfiguration: &kendra.IndexServerSideEncryptionConfigurationArgs{
KmsKeyId: pulumi.Any(this.Arn),
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var example = new Aws.Kendra.Index("example", new()
{
Name = "example",
RoleArn = thisAwsIamRole.Arn,
ServerSideEncryptionConfiguration = new Aws.Kendra.Inputs.IndexServerSideEncryptionConfigurationArgs
{
KmsKeyId = @this.Arn,
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.kendra.Index;
import com.pulumi.aws.kendra.IndexArgs;
import com.pulumi.aws.kendra.inputs.IndexServerSideEncryptionConfigurationArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var example = new Index("example", IndexArgs.builder()
.name("example")
.roleArn(thisAwsIamRole.arn())
.serverSideEncryptionConfiguration(IndexServerSideEncryptionConfigurationArgs.builder()
.kmsKeyId(this_.arn())
.build())
.build());
}
}
resources:
example:
type: aws:kendra:Index
properties:
name: example
roleArn: ${thisAwsIamRole.arn}
serverSideEncryptionConfiguration:
kmsKeyId: ${this.arn}
The serverSideEncryptionConfiguration block specifies the KMS key ARN. Kendra encrypts all indexed data with this key. Note that this configuration is immutable; you cannot change the encryption key after index creation.
Integrate with AWS SSO for access control
When search results need to respect organizational access controls, Kendra can fetch user and group information from AWS SSO.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.kendra.Index("example", {
name: "example",
roleArn: _this.arn,
userGroupResolutionConfiguration: {
userGroupResolutionMode: "AWS_SSO",
},
});
import pulumi
import pulumi_aws as aws
example = aws.kendra.Index("example",
name="example",
role_arn=this["arn"],
user_group_resolution_configuration={
"user_group_resolution_mode": "AWS_SSO",
})
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/kendra"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := kendra.NewIndex(ctx, "example", &kendra.IndexArgs{
Name: pulumi.String("example"),
RoleArn: pulumi.Any(this.Arn),
UserGroupResolutionConfiguration: &kendra.IndexUserGroupResolutionConfigurationArgs{
UserGroupResolutionMode: pulumi.String("AWS_SSO"),
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var example = new Aws.Kendra.Index("example", new()
{
Name = "example",
RoleArn = @this.Arn,
UserGroupResolutionConfiguration = new Aws.Kendra.Inputs.IndexUserGroupResolutionConfigurationArgs
{
UserGroupResolutionMode = "AWS_SSO",
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.kendra.Index;
import com.pulumi.aws.kendra.IndexArgs;
import com.pulumi.aws.kendra.inputs.IndexUserGroupResolutionConfigurationArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var example = new Index("example", IndexArgs.builder()
.name("example")
.roleArn(this_.arn())
.userGroupResolutionConfiguration(IndexUserGroupResolutionConfigurationArgs.builder()
.userGroupResolutionMode("AWS_SSO")
.build())
.build());
}
}
resources:
example:
type: aws:kendra:Index
properties:
name: example
roleArn: ${this.arn}
userGroupResolutionConfiguration:
userGroupResolutionMode: AWS_SSO
The userGroupResolutionConfiguration block enables SSO integration. Setting userGroupResolutionMode to AWS_SSO tells Kendra to fetch group memberships from your SSO identity source. Search results are then filtered based on the authenticated user’s group access.
Configure JWT-based user authentication
Applications that authenticate users with JSON Web Tokens can pass user identity and group membership to Kendra.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.kendra.Index("example", {
name: "example",
roleArn: _this.arn,
userTokenConfigurations: {
jsonTokenTypeConfiguration: {
groupAttributeField: "groups",
userNameAttributeField: "username",
},
},
});
import pulumi
import pulumi_aws as aws
example = aws.kendra.Index("example",
name="example",
role_arn=this["arn"],
user_token_configurations={
"json_token_type_configuration": {
"group_attribute_field": "groups",
"user_name_attribute_field": "username",
},
})
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/kendra"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := kendra.NewIndex(ctx, "example", &kendra.IndexArgs{
Name: pulumi.String("example"),
RoleArn: pulumi.Any(this.Arn),
UserTokenConfigurations: &kendra.IndexUserTokenConfigurationsArgs{
JsonTokenTypeConfiguration: &kendra.IndexUserTokenConfigurationsJsonTokenTypeConfigurationArgs{
GroupAttributeField: pulumi.String("groups"),
UserNameAttributeField: pulumi.String("username"),
},
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var example = new Aws.Kendra.Index("example", new()
{
Name = "example",
RoleArn = @this.Arn,
UserTokenConfigurations = new Aws.Kendra.Inputs.IndexUserTokenConfigurationsArgs
{
JsonTokenTypeConfiguration = new Aws.Kendra.Inputs.IndexUserTokenConfigurationsJsonTokenTypeConfigurationArgs
{
GroupAttributeField = "groups",
UserNameAttributeField = "username",
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.kendra.Index;
import com.pulumi.aws.kendra.IndexArgs;
import com.pulumi.aws.kendra.inputs.IndexUserTokenConfigurationsArgs;
import com.pulumi.aws.kendra.inputs.IndexUserTokenConfigurationsJsonTokenTypeConfigurationArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var example = new Index("example", IndexArgs.builder()
.name("example")
.roleArn(this_.arn())
.userTokenConfigurations(IndexUserTokenConfigurationsArgs.builder()
.jsonTokenTypeConfiguration(IndexUserTokenConfigurationsJsonTokenTypeConfigurationArgs.builder()
.groupAttributeField("groups")
.userNameAttributeField("username")
.build())
.build())
.build());
}
}
resources:
example:
type: aws:kendra:Index
properties:
name: example
roleArn: ${this.arn}
userTokenConfigurations:
jsonTokenTypeConfiguration:
groupAttributeField: groups
userNameAttributeField: username
The userTokenConfigurations block defines how Kendra extracts user information from JWTs. The groupAttributeField and userNameAttributeField properties specify which JWT claims contain group membership and username. Kendra uses these claims to filter search results per user.
Beyond these examples
These snippets focus on specific index-level features: index creation and edition selection, capacity scaling and encryption, and access control integration. They’re intentionally minimal rather than full search applications.
The examples may reference pre-existing infrastructure such as IAM roles with CloudWatch and S3 permissions, KMS keys for encryption, and AWS SSO configuration for user group resolution. They focus on configuring the index rather than provisioning everything around it.
To keep things focused, common index patterns are omitted, including:
- Document metadata field configuration (documentMetadataConfigurationUpdates)
- User context policies (userContextPolicy)
- Custom metadata fields for search faceting and relevance tuning
- Data source connections and document ingestion
These omissions are intentional: the goal is to illustrate how each index feature is wired, not provide drop-in search modules. See the Kendra Index resource reference for all available configuration options.
Let's create AWS Kendra Search Indexes
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Immutability & Limitations
edition property is immutable. Choose DEVELOPER_EDITION for development/testing/POC, ENTERPRISE_EDITION for production (default), or GEN_AI_ENTERPRISE_EDITION for generative AI applications before creating the index.serverSideEncryptionConfiguration with a symmetric KMS key ARN.documentMetadataConfigurationUpdates blocks are permanent.Metadata Configuration
documentMetadataConfigurationUpdates, you must define all 14 predefined fields, even if using defaults. Omitting any will cause configuration errors._authors, _category, _created_at, _data_source_id, _document_title, _excerpt_page_number, _faq_id, _file_type, _language_code, _last_updated_at, _source_uri, _tenant_id, _version, and _view_count.documentMetadataConfigurationUpdates alongside the 14 predefined fields. Specify the field type (STRING_VALUE, LONG_VALUE, STRING_LIST_VALUE, or DATE_VALUE) and configure search and relevance settings.documentMetadataConfigurationUpdates.Capacity & Scaling
capacityUnits with queryCapacityUnits and storageCapacityUnits to add document storage and query capacity beyond the base allocation.User Context & Access Control
userContextPolicy defaults to ATTRIBUTE_FILTER. You can also set it to USER_TOKEN for token-based access control.userGroupResolutionConfiguration with userGroupResolutionMode set to AWS_SSO to fetch access levels from AWS Single Sign-On.Troubleshooting
status output field. When status is FAILED, the errorMessage field contains details about why the operation failed.Using a different cloud?
Explore analytics guides for other cloud providers: